SEBSERV-287 back-end implementation

This commit is contained in:
anhefti 2022-04-13 14:21:27 +02:00
parent 3d94637300
commit 191f8432de
22 changed files with 907 additions and 273 deletions

View file

@ -65,8 +65,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamCon
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetDefaultExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
@ -143,7 +143,7 @@ public class ExamForm implements TemplateComposer {
private final PageService pageService;
private final ResourceService resourceService;
private final ExamSEBRestrictionSettings examSEBRestrictionSettings;
private final ExamProctoringSettings examProctoringSettings;
private final ProctoringSettingsPopup proctoringSettingsPopup;
private final WidgetFactory widgetFactory;
private final RestService restService;
private final ExamDeletePopup examDeletePopup;
@ -154,7 +154,7 @@ public class ExamForm implements TemplateComposer {
protected ExamForm(
final PageService pageService,
final ExamSEBRestrictionSettings examSEBRestrictionSettings,
final ExamProctoringSettings examProctoringSettings,
final ProctoringSettingsPopup proctoringSettingsPopup,
final ExamToConfigBindingForm examToConfigBindingForm,
final DownloadService downloadService,
final ExamDeletePopup examDeletePopup,
@ -165,7 +165,7 @@ public class ExamForm implements TemplateComposer {
this.pageService = pageService;
this.resourceService = pageService.getResourceService();
this.examSEBRestrictionSettings = examSEBRestrictionSettings;
this.examProctoringSettings = examProctoringSettings;
this.proctoringSettingsPopup = proctoringSettingsPopup;
this.widgetFactory = pageService.getWidgetFactory();
this.restService = this.resourceService.getRestService();
this.examDeletePopup = examDeletePopup;
@ -390,7 +390,7 @@ public class ExamForm implements TemplateComposer {
}
final boolean proctoringEnabled = importFromQuizData ? false : this.restService
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.map(ProctoringServiceSettings::getEnableProctoring)
@ -455,13 +455,13 @@ public class ExamForm implements TemplateComposer {
.newAction(ActionDefinition.EXAM_PROCTORING_ON)
.withEntityKey(entityKey)
.withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable))
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> proctoringEnabled && readonly)
.newAction(ActionDefinition.EXAM_PROCTORING_OFF)
.withEntityKey(entityKey)
.withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable))
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> !proctoringEnabled && readonly);

View file

@ -44,15 +44,15 @@ import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplateProctoringSettings;
@Lazy
@Component
@GuiProfile
public class ExamProctoringSettings {
public class ExamTemplateProctoringSettingsPopup {
private static final Logger log = LoggerFactory.getLogger(ExamProctoringSettings.class);
private static final Logger log = LoggerFactory.getLogger(ExamTemplateProctoringSettingsPopup.class);
private final static LocTextKey SEB_PROCTORING_FORM_TITLE =
new LocTextKey("sebserver.exam.proctoring.form.title");
@ -178,7 +178,7 @@ public class ExamProctoringSettings {
final boolean saveOk = !pageService
.getRestService()
.getBuilder(SaveProctoringSettings.class)
.getBuilder(SaveExamTemplateProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withBody(examProctoring)
.call()
@ -227,7 +227,7 @@ public class ExamProctoringSettings {
.createPopupScrollComposite(parent);
final ProctoringServiceSettings proctoringSettings = restService
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamTemplateProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOrThrow();
@ -329,24 +329,6 @@ public class ExamProctoringSettings {
return () -> formHandle;
}
// TODO
// private void procServiceSelection(final Form form) {
// final ProctoringServerType proctoringServerType = ProctoringServerType
// .valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
// switch (proctoringServerType) {
// case ZOOM: {
// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_KEY);
// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_SECRET);
// break;
// }
// default: {
// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_KEY);
// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_SECRET);
// }
// }
// }
}
}

View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2020 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.gui.content.exam;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.form.Form;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
import ch.ethz.seb.sebserver.gui.form.FormHandle;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplateProctoringSettings;
@Lazy
@Component
@GuiProfile
public class ProctoringSettingsPopup {
private static final Logger log = LoggerFactory.getLogger(ProctoringSettingsPopup.class);
private final static LocTextKey SEB_PROCTORING_FORM_TITLE =
new LocTextKey("sebserver.exam.proctoring.form.title");
private final static LocTextKey SEB_PROCTORING_FORM_INFO =
new LocTextKey("sebserver.exam.proctoring.form.info");
private final static LocTextKey SEB_PROCTORING_FORM_INFO_TITLE =
new LocTextKey("sebserver.exam.proctoring.form.info.title");
private final static LocTextKey SEB_PROCTORING_FORM_ENABLE =
new LocTextKey("sebserver.exam.proctoring.form.enabled");
private final static LocTextKey SEB_PROCTORING_FORM_TYPE =
new LocTextKey("sebserver.exam.proctoring.form.type");
private final static LocTextKey SEB_PROCTORING_FORM_URL =
new LocTextKey("sebserver.exam.proctoring.form.url");
private final static LocTextKey SEB_PROCTORING_FORM_ROOM_SIZE =
new LocTextKey("sebserver.exam.proctoring.form.collectingRoomSize");
private final static LocTextKey SEB_PROCTORING_FORM_APPKEY =
new LocTextKey("sebserver.exam.proctoring.form.appkey");
private final static LocTextKey SEB_PROCTORING_FORM_SECRET =
new LocTextKey("sebserver.exam.proctoring.form.secret");
private final static LocTextKey SEB_PROCTORING_FORM_SDKKEY =
new LocTextKey("sebserver.exam.proctoring.form.sdkkey");
private final static LocTextKey SEB_PROCTORING_FORM_SDKSECRET =
new LocTextKey("sebserver.exam.proctoring.form.sdksecret");
private final static LocTextKey SEB_PROCTORING_FORM_USE_ZOOM_APP_CLIENT =
new LocTextKey("sebserver.exam.proctoring.form.useZoomAppClient");
private final static LocTextKey SEB_PROCTORING_FORM_FEATURES =
new LocTextKey("sebserver.exam.proctoring.form.features");
Function<PageAction, PageAction> settingsFunction(final PageService pageService, final boolean modifyGrant) {
return action -> {
final PageContext pageContext = action.pageContext()
.withAttribute(
PageContext.AttributeKeys.FORCE_READ_ONLY,
(modifyGrant) ? Constants.FALSE_STRING : Constants.TRUE_STRING);
final ModalInputDialog<FormHandle<?>> dialog =
new ModalInputDialog<FormHandle<?>>(
action.pageContext().getParent().getShell(),
pageService.getWidgetFactory())
.setDialogWidth(740)
.setDialogHeight(400);
final SEBProctoringPropertiesForm bindFormContext = new SEBProctoringPropertiesForm(
pageService,
pageContext);
final Predicate<FormHandle<?>> doBind = formHandle -> doSaveSettings(
pageService,
pageContext,
formHandle);
if (modifyGrant) {
dialog.open(
SEB_PROCTORING_FORM_TITLE,
doBind,
Utils.EMPTY_EXECUTION,
bindFormContext);
} else {
dialog.open(
SEB_PROCTORING_FORM_TITLE,
pageContext,
pc -> bindFormContext.compose(pc.getParent()));
}
return action;
};
}
private boolean doSaveSettings(
final PageService pageService,
final PageContext pageContext,
final FormHandle<?> formHandle) {
final boolean isReadonly = BooleanUtils.toBoolean(
pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
if (isReadonly) {
return true;
}
final EntityKey entityKey = pageContext.getEntityKey();
ProctoringServiceSettings examProctoring = null;
try {
final Form form = formHandle.getForm();
form.clearErrors();
final boolean enabled = BooleanUtils.toBoolean(
form.getFieldValue(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING));
final ProctoringServerType serverType = ProctoringServerType
.valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
final String features = form.getFieldValue(ProctoringServiceSettings.ATTR_ENABLED_FEATURES);
final EnumSet<ProctoringFeature> featureFlags = (StringUtils.isNotBlank(features))
? EnumSet.copyOf(Arrays.asList(StringUtils.split(features, Constants.LIST_SEPARATOR))
.stream()
.map(str -> ProctoringFeature.valueOf(str))
.collect(Collectors.toSet()))
: EnumSet.noneOf(ProctoringFeature.class);
examProctoring = new ProctoringServiceSettings(
Long.parseLong(entityKey.modelId),
enabled,
serverType,
form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_URL),
Integer.parseInt(form.getFieldValue(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)),
featureFlags,
false,
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_KEY),
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_SECRET),
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_KEY),
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_SECRET),
BooleanUtils.toBoolean(form.getFieldValue(
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM)));
} catch (final Exception e) {
log.error("Unexpected error while trying to get settings from form: ", e);
}
if (examProctoring == null) {
return false;
}
final boolean saveOk = !pageService
.getRestService()
.getBuilder(
entityKey.entityType == EntityType.EXAM
? SaveExamProctoringSettings.class
: SaveExamTemplateProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.withBody(examProctoring)
.call()
.onError(formHandle::handleError)
.hasError();
if (saveOk) {
final PageAction action = pageService.pageActionBuilder(pageContext)
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
.create();
pageService.firePageEvent(
new ActionEvent(action),
action.pageContext());
return true;
}
return false;
}
private final class SEBProctoringPropertiesForm
implements ModalInputDialogComposer<FormHandle<?>> {
private final PageService pageService;
private final PageContext pageContext;
protected SEBProctoringPropertiesForm(
final PageService pageService,
final PageContext pageContext) {
this.pageService = pageService;
this.pageContext = pageContext;
}
@Override
public Supplier<FormHandle<?>> compose(final Composite parent) {
final RestService restService = this.pageService.getRestService();
final ResourceService resourceService = this.pageService.getResourceService();
final EntityKey entityKey = this.pageContext.getEntityKey();
final boolean isReadonly = BooleanUtils.toBoolean(
this.pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
final Composite content = this.pageService
.getWidgetFactory()
.createPopupScrollComposite(parent);
final ProctoringServiceSettings proctoringSettings = restService
.getBuilder(
entityKey.entityType == EntityType.EXAM
? GetExamProctoringSettings.class
: GetExamTemplateProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOrThrow();
final PageContext formContext = this.pageContext
.copyOf(content)
.clearEntityKeys();
final FormHandle<ProctoringServiceSettings> formHandle = this.pageService.formBuilder(
formContext)
.withDefaultSpanInput(5)
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(1)
.readonly(isReadonly)
.addField(FormBuilder.text(
"Info",
SEB_PROCTORING_FORM_INFO_TITLE,
this.pageService.getI18nSupport().getText(SEB_PROCTORING_FORM_INFO))
.asArea(50)
.asHTML()
.readonly(true))
.addField(FormBuilder.checkbox(
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
SEB_PROCTORING_FORM_ENABLE,
String.valueOf(proctoringSettings.enableProctoring)))
.addField(FormBuilder.singleSelection(
ProctoringServiceSettings.ATTR_SERVER_TYPE,
SEB_PROCTORING_FORM_TYPE,
proctoringSettings.serverType.name(),
resourceService::examProctoringTypeResources))
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_SERVER_URL,
SEB_PROCTORING_FORM_URL,
proctoringSettings.serverURL)
.mandatory())
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_APP_KEY,
SEB_PROCTORING_FORM_APPKEY,
proctoringSettings.appKey)
.mandatory())
.withEmptyCellSeparation(false)
.addField(FormBuilder.password(
ProctoringServiceSettings.ATTR_APP_SECRET,
SEB_PROCTORING_FORM_SECRET,
(proctoringSettings.appSecret != null)
? String.valueOf(proctoringSettings.appSecret)
: null)
.mandatory())
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_SDK_KEY,
SEB_PROCTORING_FORM_SDKKEY,
proctoringSettings.sdkKey))
.withEmptyCellSeparation(false)
.addField(FormBuilder.password(
ProctoringServiceSettings.ATTR_SDK_SECRET,
SEB_PROCTORING_FORM_SDKSECRET,
(proctoringSettings.sdkSecret != null)
? String.valueOf(proctoringSettings.sdkSecret)
: null))
.withDefaultSpanInput(1)
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
SEB_PROCTORING_FORM_ROOM_SIZE,
String.valueOf(proctoringSettings.getCollectingRoomSize()))
.asNumber(numString -> Long.parseLong(numString)))
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(4)
.withDefaultSpanInput(5)
.addField(FormBuilder.checkbox(
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
SEB_PROCTORING_FORM_USE_ZOOM_APP_CLIENT,
String.valueOf(proctoringSettings.useZoomAppClientForCollectingRoom)))
.withDefaultSpanInput(5)
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(1)
.addField(FormBuilder.multiCheckboxSelection(
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
SEB_PROCTORING_FORM_FEATURES,
StringUtils.join(proctoringSettings.enabledFeatures, Constants.LIST_SEPARATOR),
resourceService::examProctoringFeaturesResources))
.build();
if (proctoringSettings.serviceInUse) {
formHandle.getForm().getFieldInput(ProctoringServiceSettings.ATTR_SERVER_TYPE).setEnabled(false);
formHandle.getForm().getFieldInput(ProctoringServiceSettings.ATTR_SERVER_URL).setEnabled(false);
}
return () -> formHandle;
}
}
}

View file

@ -53,7 +53,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData;
@ -376,7 +376,7 @@ public class MonitoringClientConnection implements TemplateComposer {
if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) {
final ProctoringServiceSettings proctoringSettings = restService
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))

View file

@ -56,7 +56,7 @@ import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
import ch.ethz.seb.sebserver.gui.service.session.FullPageMonitoringGUIUpdate;
@ -243,7 +243,7 @@ public class MonitoringRunningExam implements TemplateComposer {
isExamSupporter));
final ProctoringServiceSettings proctoringSettings = this.restService
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOr(null);

View file

@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -69,7 +69,7 @@ public class JitsiMeetProctoringView extends AbstractProctoringView {
.getProctoringGUIService();
final ProctoringServiceSettings proctoringSettings = this.pageService
.getRestService()
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))

View file

@ -32,7 +32,7 @@ import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -69,7 +69,7 @@ public class ZoomProctoringView extends AbstractProctoringView {
.getProctoringGUIService();
final ProctoringServiceSettings proctoringSettings = this.pageService
.getRestService()
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))

View file

@ -24,9 +24,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetProctoringSettings extends RestCall<ProctoringServiceSettings> {
public class GetExamProctoringSettings extends RestCall<ProctoringServiceSettings> {
public GetProctoringSettings() {
public GetExamProctoringSettings() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA,

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 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.gui.service.remote.webservice.api.exam;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetExamTemplateProctoringSettings extends RestCall<ProctoringServiceSettings> {
public GetExamTemplateProctoringSettings() {
super(new TypeKey<>(
CallType.GET_SINGLE,
EntityType.EXAM_PROCTOR_DATA,
new TypeReference<ProctoringServiceSettings>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_JSON,
API.EXAM_TEMPLATE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT);
}
}

View file

@ -17,20 +17,20 @@ import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SaveProctoringSettings extends RestCall<Exam> {
public class SaveExamProctoringSettings extends RestCall<ProctoringServiceSettings> {
public SaveProctoringSettings() {
public SaveExamProctoringSettings() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.EXAM_PROCTOR_DATA,
new TypeReference<Exam>() {
new TypeReference<ProctoringServiceSettings>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_JSON,

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 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.gui.service.remote.webservice.api.exam;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SaveExamTemplateProctoringSettings extends RestCall<ProctoringServiceSettings> {
public SaveExamTemplateProctoringSettings() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.EXAM_PROCTOR_DATA,
new TypeReference<ProctoringServiceSettings>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_JSON,
API.EXAM_TEMPLATE_ENDPOINT
+ API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT);
}
}

View file

@ -13,6 +13,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
@ -22,6 +23,18 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttribut
* in a separated data-base table. */
public interface AdditionalAttributesDAO {
/** Use this to get all additional attribute records for a specific entity.
*
* @param type the entity type
* @param entityId the entity identifier (primary key)
* @return Result refer to the collection of additional attribute records or to an error if happened */
default Result<Collection<AdditionalAttributeRecord>> getAdditionalAttributes(final EntityKey entityKey) {
return Result.tryCatch(() -> getAdditionalAttributes(
entityKey.entityType,
Long.valueOf(entityKey.modelId))
.getOrThrow());
}
/** Use this to get all additional attribute records for a specific entity.
*
* @param type the entity type

View file

@ -12,7 +12,6 @@ import org.apache.commons.lang3.BooleanUtils;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
@ -83,19 +82,10 @@ public interface ExamAdminService {
* @return Result refer to proctoring is enabled flag or to an error when happened. */
Result<Boolean> isProctoringEnabled(final Long examId);
/** Get the exam proctoring service implementation of specified type.
*
* @param type exam proctoring service server type
* @return ExamProctoringService instance */
Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
/** Get the exam proctoring service implementation for specified exam.
*
* @param examId the exam identifier
* @return ExamProctoringService instance */
default Result<ExamProctoringService> getExamProctoringService(final Long examId) {
return getProctoringServiceSettings(examId)
.flatMap(settings -> getExamProctoringService(settings.serverType));
}
Result<ExamProctoringService> getExamProctoringService(final Long examId);
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2022 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.exam;
import java.util.EnumSet;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
public interface ProctoringAdminService {
EnumSet<EntityType> SUPPORTED_PARENT_ENTITES = EnumSet.of(
EntityType.EXAM,
EntityType.EXAM_TEMPLATE);
/** Get proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES).
*
* @param parentEntityKey the entity key of the parent entity to get the proctoring service settings from
* @return Result refer to proctoring service settings or to an error when happened. */
Result<ProctoringServiceSettings> getProctoringSettings(EntityKey parentEntityKey);
/** Save the given proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES).
*
* @param parentEntityKey the entity key of the parent entity to save the proctoring service settings to
* @param proctoringServiceSettings The proctoring service settings to save
* @return Result refer to saved proctoring service settings or to an error when happened. */
Result<ProctoringServiceSettings> saveProctoringServiceSettings(
EntityKey parentEntityKey,
ProctoringServiceSettings proctoringServiceSettings);
/** Get the exam proctoring service implementation of specified type.
*
* @param type exam proctoring service server type
* @return ExamProctoringService instance */
Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
/** Use this to test the proctoring service settings against the remote proctoring server.
*
* @param proctoringSettings the settings to test
* @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
default Result<Boolean> testExamProctoring(final ProctoringServiceSettings proctoringSettings) {
return getExamProctoringService(proctoringSettings.serverType)
.flatMap(service -> service.testExamProctoring(proctoringSettings));
}
}

View file

@ -9,12 +9,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -26,28 +21,23 @@ import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
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.OpenEdxSEBRestriction;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory;
@Lazy
@Service
@ -57,26 +47,20 @@ public class ExamAdminServiceImpl implements ExamAdminService {
private static final Logger log = LoggerFactory.getLogger(ExamAdminServiceImpl.class);
private final ExamDAO examDAO;
private final ProctoringAdminService proctoringServiceSettingsService;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final LmsAPIService lmsAPIService;
private final Cryptor cryptor;
private final ExamProctoringServiceFactory examProctoringServiceFactory;
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
protected ExamAdminServiceImpl(
final ExamDAO examDAO,
final ProctoringAdminService proctoringServiceSettingsService,
final AdditionalAttributesDAO additionalAttributesDAO,
final LmsAPIService lmsAPIService,
final Cryptor cryptor,
final ExamProctoringServiceFactory examProctoringServiceFactory,
final RemoteProctoringRoomDAO remoteProctoringRoomDAO) {
final LmsAPIService lmsAPIService) {
this.examDAO = examDAO;
this.proctoringServiceSettingsService = proctoringServiceSettingsService;
this.additionalAttributesDAO = additionalAttributesDAO;
this.lmsAPIService = lmsAPIService;
this.cryptor = cryptor;
this.examProctoringServiceFactory = examProctoringServiceFactory;
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
}
@Override
@ -135,26 +119,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
@Override
public Result<ProctoringServiceSettings> getProctoringServiceSettings(final Long examId) {
return this.additionalAttributesDAO.getAdditionalAttributes(EntityType.EXAM, examId)
.map(attrs -> attrs.stream()
.collect(Collectors.toMap(
attr -> attr.getName(),
Function.identity())))
.map(mapping -> {
return new ProctoringServiceSettings(
examId,
getEnabled(mapping),
getServerType(mapping),
getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL),
getCollectingRoomSize(mapping),
getEnabledFeatures(mapping),
this.remoteProctoringRoomDAO.isServiceInUse(examId).getOr(true),
getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY),
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET),
getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY),
getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET),
getBoolean(mapping, ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM));
});
return this.proctoringServiceSettingsService.getProctoringSettings(new EntityKey(examId, EntityType.EXAM));
}
@Override
@ -163,79 +128,14 @@ public class ExamAdminServiceImpl implements ExamAdminService {
final Long examId,
final ProctoringServiceSettings proctoringServiceSettings) {
return Result.tryCatch(() -> {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
String.valueOf(proctoringServiceSettings.enableProctoring));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_SERVER_TYPE,
proctoringServiceSettings.serverType.name());
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_SERVER_URL,
StringUtils.trim(proctoringServiceSettings.serverURL));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
String.valueOf(proctoringServiceSettings.collectingRoomSize));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_APP_KEY,
StringUtils.trim(proctoringServiceSettings.appKey));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_APP_SECRET,
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret))
.getOrThrow()
.toString());
if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_SDK_KEY,
StringUtils.trim(proctoringServiceSettings.sdkKey));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_SDK_SECRET,
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret))
.getOrThrow()
.toString());
}
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR));
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom));
// Mark the involved exam as updated to notify changes
this.examDAO.setModified(examId);
return proctoringServiceSettings;
});
return this.proctoringServiceSettingsService
.saveProctoringServiceSettings(
new EntityKey(examId, EntityType.EXAM),
proctoringServiceSettings)
.map(settings -> {
this.examDAO.setModified(examId);
return settings;
});
}
@Override
@ -256,78 +156,10 @@ public class ExamAdminServiceImpl implements ExamAdminService {
}
@Override
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
return this.examProctoringServiceFactory
.getExamProctoringService(type);
}
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) {
return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue());
} else {
return false;
}
}
private ProctoringServerType getServerType(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) {
return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue());
} else {
return ProctoringServerType.JITSI_MEET;
}
}
private String getString(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
if (mapping.containsKey(name)) {
return mapping.get(name).getValue();
} else {
return null;
}
}
private Boolean getBoolean(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
if (mapping.containsKey(name)) {
return BooleanUtils.toBooleanObject(mapping.get(name).getValue());
} else {
return false;
}
}
private Integer getCollectingRoomSize(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) {
return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue());
} else {
return 20;
}
}
private EnumSet<ProctoringFeature> getEnabledFeatures(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) {
try {
final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES).getValue();
return StringUtils.isNotBlank(value)
? EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR))
.stream()
.map(str -> {
try {
return ProctoringFeature.valueOf(str);
} catch (final Exception e) {
log.error(
"Failed to enabled single features for proctoring settings. Skipping. {}",
e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet()))
: EnumSet.noneOf(ProctoringFeature.class);
} catch (final Exception e) {
log.error("Failed to get enabled features for proctoring settings. Enable all. {}", e.getMessage());
return EnumSet.allOf(ProctoringFeature.class);
}
} else {
return EnumSet.allOf(ProctoringFeature.class);
}
public Result<ExamProctoringService> getExamProctoringService(final Long examId) {
return getProctoringServiceSettings(examId)
.flatMap(settings -> this.proctoringServiceSettingsService
.getExamProctoringService(settings.serverType));
}
private Result<Exam> saveAdditionalAttributesForMoodleExams(final Exam exam) {

View file

@ -118,7 +118,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
@Override
public Result<Exam> initAdditionalAttributes(final Exam exam) {
return this.examAdminService.saveLMSAttributes(exam)
return this.examAdminService
.saveLMSAttributes(exam)
.map(_exam -> {
if (exam.examTemplateId != null) {
@ -148,7 +149,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
}
}
return _exam;
}).onError(error -> log.error("Failed to create additional attributes defined by template for exam: ",
}).onError(error -> log.error(
"Failed to create additional attributes defined by template for exam: ",
error));
}

View file

@ -0,0 +1,270 @@
/*
* Copyright (c) 2022 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.exam.impl;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory;
@Lazy
@Service
@WebServiceProfile
public class ProctoringAdminServiceImpl implements ProctoringAdminService {
private static final Logger log = LoggerFactory.getLogger(ProctoringAdminServiceImpl.class);
private final AdditionalAttributesDAO additionalAttributesDAO;
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
private final ExamProctoringServiceFactory examProctoringServiceFactory;
private final Cryptor cryptor;
public ProctoringAdminServiceImpl(
final AdditionalAttributesDAO additionalAttributesDAO,
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
final ExamProctoringServiceFactory examProctoringServiceFactory,
final Cryptor cryptor) {
this.additionalAttributesDAO = additionalAttributesDAO;
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
this.examProctoringServiceFactory = examProctoringServiceFactory;
this.cryptor = cryptor;
}
@Override
@Transactional(readOnly = true)
public Result<ProctoringServiceSettings> getProctoringSettings(final EntityKey parentEntityKey) {
return Result.tryCatch(() -> {
final Long entityId = Long.parseLong(parentEntityKey.modelId);
checkType(parentEntityKey);
return this.additionalAttributesDAO
.getAdditionalAttributes(parentEntityKey.entityType, entityId)
.map(attrs -> attrs.stream()
.collect(Collectors.toMap(
attr -> attr.getName(),
Function.identity())))
.map(mapping -> {
return new ProctoringServiceSettings(
entityId,
getEnabled(mapping),
getServerType(mapping),
getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL),
getCollectingRoomSize(mapping),
getEnabledFeatures(mapping),
parentEntityKey.entityType == EntityType.EXAM
? this.remoteProctoringRoomDAO.isServiceInUse(entityId).getOr(true)
: false,
getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY),
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET),
getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY),
getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET),
getBoolean(mapping,
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM));
})
.getOrThrow();
});
}
@Override
@Transactional
public Result<ProctoringServiceSettings> saveProctoringServiceSettings(
final EntityKey parentEntityKey,
final ProctoringServiceSettings proctoringServiceSettings) {
return Result.tryCatch(() -> {
final Long entityId = Long.parseLong(parentEntityKey.modelId);
checkType(parentEntityKey);
if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) {
testExamProctoring(proctoringServiceSettings).getOrThrow();
}
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
String.valueOf(proctoringServiceSettings.enableProctoring));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_SERVER_TYPE,
proctoringServiceSettings.serverType.name());
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_SERVER_URL,
StringUtils.trim(proctoringServiceSettings.serverURL));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
String.valueOf(proctoringServiceSettings.collectingRoomSize));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_APP_KEY,
StringUtils.trim(proctoringServiceSettings.appKey));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_APP_SECRET,
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret))
.getOrThrow()
.toString());
if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) {
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_SDK_KEY,
StringUtils.trim(proctoringServiceSettings.sdkKey));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_SDK_SECRET,
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret))
.getOrThrow()
.toString());
}
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR));
this.additionalAttributesDAO.saveAdditionalAttribute(
parentEntityKey.entityType,
entityId,
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom));
return proctoringServiceSettings;
});
}
@Override
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
return this.examProctoringServiceFactory
.getExamProctoringService(type);
}
private void checkType(final EntityKey parentEntityKey) {
if (!SUPPORTED_PARENT_ENTITES.contains(parentEntityKey.entityType)) {
throw new UnsupportedOperationException(
"No proctoring service settings supported for entity: " + parentEntityKey);
}
}
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) {
return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue());
} else {
return false;
}
}
private ProctoringServerType getServerType(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) {
return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue());
} else {
return ProctoringServerType.JITSI_MEET;
}
}
private String getString(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
if (mapping.containsKey(name)) {
return mapping.get(name).getValue();
} else {
return null;
}
}
private Boolean getBoolean(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
if (mapping.containsKey(name)) {
return BooleanUtils.toBooleanObject(mapping.get(name).getValue());
} else {
return false;
}
}
private Integer getCollectingRoomSize(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) {
return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue());
} else {
return 20;
}
}
private EnumSet<ProctoringFeature> getEnabledFeatures(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) {
try {
final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES).getValue();
return StringUtils.isNotBlank(value)
? EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR))
.stream()
.map(str -> {
try {
return ProctoringFeature.valueOf(str);
} catch (final Exception e) {
log.error(
"Failed to enabled single features for proctoring settings. Skipping. {}",
e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet()))
: EnumSet.noneOf(ProctoringFeature.class);
} catch (final Exception e) {
log.error("Failed to get enabled features for proctoring settings. Enable all. {}", e.getMessage());
return EnumSet.allOf(ProctoringFeature.class);
}
} else {
return EnumSet.allOf(ProctoringFeature.class);
}
}
}

View file

@ -40,6 +40,7 @@ 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.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
@ -58,6 +59,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
private final ClientConnectionDAO clientConnectionDAO;
private final ExamAdminService examAdminService;
private final ProctoringAdminService proctoringAdminService;
private final ExamSessionService examSessionService;
private final SEBClientInstructionService sebInstructionService;
private final boolean sendBroadcastReset;
@ -66,6 +68,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
final ClientConnectionDAO clientConnectionDAO,
final ExamAdminService examAdminService,
final ProctoringAdminService proctoringAdminService,
final ExamSessionService examSessionService,
final SEBClientInstructionService sebInstructionService,
@Value("${sebserver.webservice.proctoring.resetBroadcastOnLeav:true}") final boolean sendBroadcastReset) {
@ -73,6 +76,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
this.clientConnectionDAO = clientConnectionDAO;
this.examAdminService = examAdminService;
this.proctoringAdminService = proctoringAdminService;
this.examSessionService = examSessionService;
this.sebInstructionService = sebInstructionService;
this.sendBroadcastReset = sendBroadcastReset;
@ -146,7 +150,8 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
event.ids.forEach(examId -> {
try {
this.examAdminService.examForPK(examId)
this.examAdminService
.examForPK(examId)
.flatMap(this::disposeRoomsForExam)
.getOrThrow();
@ -176,7 +181,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(exam.id)
.getOrThrow();
this.examAdminService
this.proctoringAdminService
.getExamProctoringService(proctoringSettings.serverType)
.flatMap(service -> service.disposeServiceRoomsForExam(exam.id, proctoringSettings))
.onError(error -> log.error("Failed to dispose proctoring service rooms for exam: {} / {}",
@ -204,7 +209,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
@ -242,7 +247,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
@ -251,7 +256,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.flatMap(room -> this.remoteProctoringRoomDAO.createBreakOutRoom(examId, room, connectionTokens))
.getOrThrow();
return this.examAdminService
return this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.map(service -> sendJoinRoomBreakOutInstructions(
settings,
@ -272,7 +277,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
@ -357,7 +362,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(proctoringSettings.serverType)
.getOrThrow();
@ -401,7 +406,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(proctoringSettings.serverType)
.getOrThrow();
@ -574,7 +579,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
@ -635,7 +640,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
.getProctoringServiceSettings(examId)
.getOrThrow();
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(settings.serverType)
.getOrThrow();
@ -689,7 +694,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
final String roomName,
final String subject) {
final ExamProctoringService examProctoringService = this.examAdminService
final ExamProctoringService examProctoringService = this.proctoringAdminService
.getExamProctoringService(proctoringSettings.serverType)
.getOrThrow();

View file

@ -381,11 +381,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
.byPK(examId)
.flatMap(this.authorization::checkModify)
.map(exam -> {
if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) {
this.examAdminService.getExamProctoringService(proctoringServiceSettings.serverType)
.flatMap(service -> service.testExamProctoring(proctoringServiceSettings))
.getOrThrow();
}
this.examAdminService.saveProctoringServiceSettings(examId, proctoringServiceSettings);
return exam;
})

View file

@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
@ -48,6 +49,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
@WebServiceProfile
@ -56,6 +58,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
public class ExamTemplateController extends EntityController<ExamTemplate, ExamTemplate> {
private final ExamTemplateDAO examTemplateDAO;
private final ProctoringAdminService proctoringServiceSettingsService;
protected ExamTemplateController(
final AuthorizationService authorization,
@ -63,7 +66,8 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
final ExamTemplateDAO entityDAO,
final UserActivityLogDAO userActivityLogDAO,
final PaginationService paginationService,
final BeanValidationService beanValidationService) {
final BeanValidationService beanValidationService,
final ProctoringAdminService proctoringServiceSettingsService) {
super(
authorization,
@ -74,6 +78,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
beanValidationService);
this.examTemplateDAO = entityDAO;
this.proctoringServiceSettingsService = proctoringServiceSettingsService;
}
@RequestMapping(
@ -93,6 +98,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
.getOrThrow();
}
// ****************************************************************************
// **** Indicator
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_TEMPLATE_INDICATOR_PATH_SEGMENT,
@ -225,11 +233,65 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
// check write privilege for requested institution and concrete entityType
this.checkWritePrivilege(institutionId);
return this.examTemplateDAO.deleteIndicatorTemplate(parentModelId, modelId)
return this.examTemplateDAO
.deleteIndicatorTemplate(parentModelId, modelId)
.flatMap(this.userActivityLogDAO::logDelete)
.getOrThrow();
}
// **** Indicator
// ****************************************************************************
// ****************************************************************************
// **** Proctoring
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ProctoringServiceSettings getProctoringServiceSettings(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable final Long modelId) {
checkReadPrivilege(institutionId);
return this.proctoringServiceSettingsService
.getProctoringSettings(new EntityKey(modelId, EntityType.EXAM_TEMPLATE))
.getOrThrow();
}
@RequestMapping(
path = API.MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT,
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public ExamTemplate saveProctoringServiceSettings(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(API.PARAM_MODEL_ID) final Long examId,
@Valid @RequestBody final ProctoringServiceSettings proctoringServiceSettings) {
checkModifyPrivilege(institutionId);
return this.entityDAO
.byPK(examId)
.flatMap(this.authorization::checkModify)
.map(exam -> {
this.proctoringServiceSettingsService.saveProctoringServiceSettings(
new EntityKey(examId, EntityType.EXAM_TEMPLATE),
proctoringServiceSettings);
return exam;
})
.flatMap(this.userActivityLogDAO::logModify)
.getOrThrow();
}
// **** Proctoring
// ****************************************************************************
@Override
protected ExamTemplate createNew(final POSTMapper postParams) {
final Long institutionId = postParams.getLong(API.PARAM_INSTITUTION_ID);

View file

@ -132,6 +132,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfi
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamDependencies;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplatePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplates;
@ -140,7 +141,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetSEBRestrictionSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamConfigMapping;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTemplate;
@ -148,10 +148,10 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamConfigMapping;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveSEBRestriction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
@ -3371,15 +3371,15 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
final RestServiceImpl restService = createRestServiceForUser(
"admin",
"admin",
new GetProctoringSettings(),
new SaveProctoringSettings());
new GetExamProctoringSettings(),
new SaveExamProctoringSettings());
final Exam exam = createTestExam("admin", "admin");
assertNotNull(exam);
assertEquals("Demo Quiz 6 (MOCKUP)", exam.name);
final ProctoringServiceSettings settings = restService
.getBuilder(GetProctoringSettings.class)
.getBuilder(GetExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
.call()
.getOrThrow();
@ -3401,16 +3401,16 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
"sdkSecret",
false);
final Result<Exam> saveCall = restService
.getBuilder(SaveProctoringSettings.class)
final Result<ProctoringServiceSettings> saveCall = restService
.getBuilder(SaveExamProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
.withBody(newSettings)
.call();
if (!saveCall.hasError()) {
assertFalse(saveCall.hasError());
final Exam exam2 = saveCall.get();
assertEquals(exam2.id, exam.id);
final ProctoringServiceSettings settings2 = saveCall.get();
assertEquals(settings2.examId, exam.id);
}
}

View file

@ -63,7 +63,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT
this.examAdminService.saveProctoringServiceSettings(
2L,
new ProctoringServiceSettings(
2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, null, false,
2L, true, ProctoringServerType.JITSI_MEET, "", 1, null, false,
"app-key", "app.secret", "sdk-key", "sdk.secret", false));
assertTrue(this.examAdminService.isProctoringEnabled(2L).get());