SEBSERV-192 also improved some error handling for exam import
This commit is contained in:
parent
fad3810ba8
commit
e19e7aeb2a
10 changed files with 107 additions and 53 deletions
|
@ -49,8 +49,6 @@ public class APIMessage implements Serializable {
|
|||
PASSWORD_MISMATCH("1300", HttpStatus.BAD_REQUEST, "new password do not match confirmed password"),
|
||||
MISSING_PASSWORD("1301", HttpStatus.BAD_REQUEST, "Missing Password"),
|
||||
|
||||
BINDING_ERROR("1500", HttpStatus.BAD_REQUEST, "External binding error"),
|
||||
|
||||
EXAM_CONSISTENCY_VALIDATION_SUPPORTER("1400", HttpStatus.OK, "No Exam Supporter defined for the Exam"),
|
||||
EXAM_CONSISTENCY_VALIDATION_CONFIG("1401", HttpStatus.OK, "No SEB Exam Configuration defined for the Exam"),
|
||||
EXAM_CONSISTENCY_VALIDATION_SEB_RESTRICTION("1402", HttpStatus.OK,
|
||||
|
@ -60,10 +58,20 @@ public class APIMessage implements Serializable {
|
|||
EXAM_CONSISTENCY_VALIDATION_INVALID_ID_REFERENCE("1405", HttpStatus.OK,
|
||||
"There seems to be an invalid exam - course identifier reference. The course cannot be found"),
|
||||
|
||||
EXAM_IMPORT_ERROR_AUTO_CONFIG("1500", HttpStatus.BAD_REQUEST,
|
||||
"Failed to automatically create and link exam configuration from exam template"),
|
||||
EXAM_IMPORT_ERROR_AUTO_CONFIG_LINKING("1500", HttpStatus.BAD_REQUEST,
|
||||
"Failed to automatically link auto-generated exam configuration");
|
||||
EXTERNAL_SERVICE_BINDING_ERROR("1500", HttpStatus.BAD_REQUEST, "External binding error"),
|
||||
|
||||
EXAM_IMPORT_ERROR_AUTO_SETUP("1600", HttpStatus.PARTIAL_CONTENT,
|
||||
"Exam successfully imported but some additional initialization failed"),
|
||||
EXAM_IMPORT_ERROR_AUTO_INDICATOR("1601", HttpStatus.PARTIAL_CONTENT,
|
||||
"Failed to automatically create pre-defined indicators for the exam"),
|
||||
EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES("1602", HttpStatus.PARTIAL_CONTENT,
|
||||
"Failed to automatically create pre-defined attributes for the exam"),
|
||||
EXAM_IMPORT_ERROR_AUTO_RESTRICTION("1603", HttpStatus.PARTIAL_CONTENT,
|
||||
"Failed to automatically apply SEB restriction for the exam to the involved LMS"),
|
||||
EXAM_IMPORT_ERROR_AUTO_CONFIG("1610", HttpStatus.PARTIAL_CONTENT,
|
||||
"Failed to automatically create and link exam configuration from the exam template to the exam"),
|
||||
EXAM_IMPORT_ERROR_AUTO_CONFIG_LINKING("1611", HttpStatus.PARTIAL_CONTENT,
|
||||
"Failed to automatically link auto-generated exam configuration to the exam");
|
||||
|
||||
public final String messageCode;
|
||||
public final HttpStatus httpStatus;
|
||||
|
|
|
@ -29,6 +29,7 @@ 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.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
|
@ -521,15 +522,20 @@ public class ExamForm implements TemplateComposer {
|
|||
return processFormSave;
|
||||
|
||||
} catch (final Exception e) {
|
||||
// try to geht the created exam id
|
||||
Throwable error = e;
|
||||
if (e instanceof FormPostException) {
|
||||
error = ((FormPostException) e).getCause();
|
||||
}
|
||||
if (error instanceof RestCallError) {
|
||||
final List<APIMessage> apiMessages = ((RestCallError) error).getAPIMessages();
|
||||
if (apiMessages != null && !apiMessages.isEmpty()) {
|
||||
final APIMessage apiMessage = apiMessages.get(0);
|
||||
return handleExamImportSetupFailure(action, e);
|
||||
}
|
||||
}
|
||||
|
||||
private PageAction handleExamImportSetupFailure(final PageAction action, final Exception e) {
|
||||
Throwable error = e;
|
||||
if (e instanceof FormPostException) {
|
||||
error = ((FormPostException) e).getCause();
|
||||
}
|
||||
if (error instanceof RestCallError) {
|
||||
final List<APIMessage> apiMessages = ((RestCallError) error).getAPIMessages();
|
||||
if (apiMessages != null && !apiMessages.isEmpty()) {
|
||||
final APIMessage apiMessage = apiMessages.remove(0);
|
||||
if (ErrorMessage.EXAM_IMPORT_ERROR_AUTO_SETUP.isOf(apiMessage)) {
|
||||
final String examIdAttr = apiMessage.attributes
|
||||
.stream()
|
||||
.filter(attr -> attr.startsWith(API.PARAM_MODEL_ID))
|
||||
|
@ -539,16 +545,21 @@ public class ExamForm implements TemplateComposer {
|
|||
examIdAttr,
|
||||
Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR);
|
||||
if (API.PARAM_MODEL_ID.equals(split[0])) {
|
||||
final String additionlMessages = apiMessages.stream()
|
||||
.reduce(
|
||||
"",
|
||||
(acc, msg) -> acc + "<br/> " + msg.systemMessage,
|
||||
(acc1, acc2) -> acc1 + acc2);
|
||||
action.pageContext().publishPageMessage(
|
||||
AUTO_GEN_CONFIG_ERROR_TITLE,
|
||||
AUTO_GEN_CONFIG_ERROR_TEXT);
|
||||
new LocTextKey(AUTO_GEN_CONFIG_ERROR_TEXT.name, additionlMessages));
|
||||
return action.withEntityKey(new EntityKey(split[1], EntityType.EXAM));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
throw new RuntimeException("Error while handle exam import setup failure:", e);
|
||||
}
|
||||
|
||||
private boolean testSEBRestrictionAPI(final Exam exam) {
|
||||
|
|
|
@ -136,6 +136,8 @@ public abstract class RestCall<T> {
|
|||
responseEntity.getBody(),
|
||||
RestCall.this.typeKey.typeRef));
|
||||
|
||||
} else if (responseEntity.getStatusCode() == HttpStatus.PARTIAL_CONTENT) {
|
||||
return handleRestCallPartialResponse(responseEntity);
|
||||
} else {
|
||||
return handleRestCallError(responseEntity);
|
||||
}
|
||||
|
@ -182,6 +184,11 @@ public abstract class RestCall<T> {
|
|||
}
|
||||
}
|
||||
|
||||
private Result<T> handleRestCallPartialResponse(final ResponseEntity<String> responseEntity) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public RestCallBuilder newBuilder() {
|
||||
return new RestCallBuilder(
|
||||
this.restService.getWebserviceAPIRestTemplate(),
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ColorData {
|
|||
|
||||
public ColorData(final Display display) {
|
||||
this.defaultColor = new Color(display, new RGB(220, 220, 220), 255);
|
||||
this.color1 = new Color(display, new RGB(34, 177, 76), 255);
|
||||
this.color1 = new Color(display, new RGB(255, 255, 255), 255);
|
||||
this.color2 = new Color(display, new RGB(255, 194, 14), 255);
|
||||
this.color3 = new Color(display, new RGB(237, 28, 36), 255);
|
||||
this.darkColor = new Color(display, Constants.BLACK_RGB);
|
||||
|
@ -42,8 +42,6 @@ public class ColorData {
|
|||
switch (connectionData.clientConnection.status) {
|
||||
case ACTIVE:
|
||||
return (connectionData.missingPing) ? this.color2 : this.color1;
|
||||
case DISABLED:
|
||||
return this.color2;
|
||||
default:
|
||||
return this.defaultColor;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.springframework.stereotype.Service;
|
|||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
|
@ -149,7 +148,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
}
|
||||
}
|
||||
return _exam;
|
||||
});
|
||||
}).onError(error -> log.error("Failed to create additional attributes defined by template for exam: ",
|
||||
error));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,8 +196,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
error))
|
||||
.getOrThrow(error -> new APIMessageException(
|
||||
ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG,
|
||||
error,
|
||||
API.PARAM_MODEL_ID + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR + exam.id));
|
||||
error));
|
||||
|
||||
// map the exam configuration to the exam
|
||||
this.examConfigurationMapDAO.createNew(new ExamConfigurationMap(
|
||||
|
@ -212,8 +211,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
error))
|
||||
.getOrThrow(error -> new APIMessageException(
|
||||
ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG_LINKING,
|
||||
error,
|
||||
API.PARAM_MODEL_ID + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR + exam.id));
|
||||
error));
|
||||
|
||||
}
|
||||
} else {
|
||||
|
@ -223,7 +221,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
}
|
||||
|
||||
return exam;
|
||||
});
|
||||
}).onError(error -> log.error("Failed to create exam configuration defined by template for exam: ", error));
|
||||
}
|
||||
|
||||
private Result<Exam> addIndicatorsFromTemplate(final Exam exam) {
|
||||
|
@ -248,11 +246,10 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
|
||||
examTemplate.indicatorTemplates
|
||||
.forEach(it -> createIndicatorFromTemplate(it, exam));
|
||||
|
||||
}
|
||||
|
||||
return exam;
|
||||
});
|
||||
}).onError(error -> log.error("Failed to create indicators defined by template for exam: ", error));
|
||||
}
|
||||
|
||||
private void createIndicatorFromTemplate(final IndicatorTemplate template, final Exam exam) {
|
||||
|
@ -310,7 +307,7 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
.getOrThrow();
|
||||
|
||||
return exam;
|
||||
});
|
||||
}).onError(error -> log.error("Failed to apply default indicators for exam: ", error));
|
||||
}
|
||||
|
||||
private String replaceVars(final String template, final Exam exam, final ExamTemplate examTemplate) {
|
||||
|
|
|
@ -150,11 +150,11 @@ public class JitsiProctoringService implements ExamProctoringService {
|
|||
final ResponseEntity<String> result =
|
||||
restTemplate.getForEntity(proctoringSettings.serverURL, String.class);
|
||||
if (result.getStatusCode() != HttpStatus.OK) {
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.BINDING_ERROR);
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.EXTERNAL_SERVICE_BINDING_ERROR);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to access proctoring service: {}", e.getMessage());
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.BINDING_ERROR, e.getMessage());
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.EXTERNAL_SERVICE_BINDING_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -186,13 +186,13 @@ public class ZoomProctoringService implements ExamProctoringService {
|
|||
|
||||
if (result.getStatusCode() != HttpStatus.OK) {
|
||||
throw new APIMessageException(
|
||||
APIMessage.ErrorMessage.BINDING_ERROR,
|
||||
APIMessage.ErrorMessage.EXTERNAL_SERVICE_BINDING_ERROR,
|
||||
String.valueOf(result.getStatusCode()));
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to access Zoom service at: {}", proctoringSettings.serverURL, e);
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.BINDING_ERROR, e.getMessage());
|
||||
throw new APIMessageException(APIMessage.ErrorMessage.EXTERNAL_SERVICE_BINDING_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -35,6 +36,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
|
|||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
|
||||
|
@ -411,11 +413,42 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
|
||||
@Override
|
||||
protected Result<Exam> notifyCreated(final Exam entity) {
|
||||
return this.examTemplateService
|
||||
final List<APIMessage> errors = new ArrayList<>();
|
||||
|
||||
this.examTemplateService
|
||||
.addDefinedIndicators(entity)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_INDICATOR.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initAdditionalAttributes)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initExamConfiguration)
|
||||
.flatMap(this.examAdminService::applyAdditionalSEBRestrictions);
|
||||
.onErrorDo(error -> {
|
||||
if (error instanceof APIMessageException) {
|
||||
errors.addAll(((APIMessageException) error).getAPIMessages());
|
||||
} else {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG.of(error));
|
||||
}
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examAdminService::applyAdditionalSEBRestrictions)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_RESTRICTION.of(error));
|
||||
return entity;
|
||||
});
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
errors.add(0, ErrorMessage.EXAM_IMPORT_ERROR_AUTO_SETUP.of(
|
||||
entity.getModelId(),
|
||||
API.PARAM_MODEL_ID + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR + entity.getModelId()));
|
||||
throw new APIMessageException(errors);
|
||||
} else {
|
||||
return Result.of(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -75,11 +75,11 @@ sebserver.webservice.proctoring.resetBroadcastOnLeav=true
|
|||
sebserver.webservice.proctoring.zoom.enableWaitingRoom=false
|
||||
sebserver.webservice.proctoring.zoom.sendRejoinForCollectingRoom=true
|
||||
|
||||
# Default indicator example:
|
||||
#sebserver.webservice.api.exam.indicator.name=Ping
|
||||
#sebserver.webservice.api.exam.indicator.type=LAST_PING
|
||||
#sebserver.webservice.api.exam.indicator.color=b4b4b4
|
||||
#sebserver.webservice.api.exam.indicator.thresholds=[{'value':5000.0,'color':'22b14c'},{'value':10000.0,'color':'ff7e00'},{'value':15000.0,'color':'ed1c24'}]
|
||||
# Default Ping indicator:
|
||||
sebserver.webservice.api.exam.indicator.name=Ping
|
||||
sebserver.webservice.api.exam.indicator.type=LAST_PING
|
||||
sebserver.webservice.api.exam.indicator.color=ffffff
|
||||
sebserver.webservice.api.exam.indicator.thresholds=[{"value":5000.0,"color":"22b14c"},{"value":10000.0,"color":"ff7e00"},{"value":15000.0,"color":"ed1c24"}]
|
||||
|
||||
# Default name and description template for auto-generated exam configuration
|
||||
sebserver.webservice.configtemplate.examconfig.default.name=__startDate__ __examName__
|
||||
|
|
|
@ -446,8 +446,8 @@ sebserver.exam.consistency.missing-seb-restriction= - There is currently no SEB
|
|||
sebserver.exam.consistency.no-lms-connection= - Failed to connect to the LMS Setup of this exam yet.<br/>Please check the LMS connection within the LMS Setup.
|
||||
sebserver.exam.consistency.invalid-lms-id= - The referencing course identifier seems to be invalid.<br/>Please check if the course for this exam still exists on the LMS and the course identifier has not changed.
|
||||
sebserver.exam.confirm.remove-config=This exam is current running. The remove of the attached configuration will led to an invalid state<br/>where connecting SEB clients cannot download the configuration for the exam.<br/><br/>Are you sure to remove the configuration?
|
||||
sebserver.exam.autogen.error.config.title=Exam Import with Template
|
||||
sebserver.exam.autogen.error.config.text=There was an unexpected error while auto-create exam configuration.<br/>Please add an exam configuration manually.
|
||||
sebserver.exam.autogen.error.config.title=Exam Import Setup Failure
|
||||
sebserver.exam.autogen.error.config.text=There was an unexpected error while setting up the imported exam.<br/> Please note that the exam has successfully been imported and can be modified but one or more additional auto-setup(s) failed:<br/>{0}
|
||||
|
||||
sebserver.exam.action.list=Exam
|
||||
sebserver.exam.action.list.view=View Exam
|
||||
|
@ -1797,16 +1797,16 @@ sebserver.monitoring.exam.connection.action.instruction.quit.all.confirm=Are you
|
|||
sebserver.monitoring.exam.connection.action.instruction.disable.selected.confirm=Are you sure to disable all selected SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.instruction.disable.all.confirm=Are you sure to disable all active SEB client connections?
|
||||
sebserver.monitoring.exam.connection.action.disable=Mark As Canceled
|
||||
sebserver.monitoring.exam.connection.action.hide.requested=Hide Requested ({0})
|
||||
sebserver.monitoring.exam.connection.action.show.requested=Show Requested ({0})
|
||||
sebserver.monitoring.exam.connection.action.hide.active=Hide Active ({0})
|
||||
sebserver.monitoring.exam.connection.action.show.active=Show Active ({0})
|
||||
sebserver.monitoring.exam.connection.action.hide.closed=Hide Closed ({0})
|
||||
sebserver.monitoring.exam.connection.action.show.closed=Show Closed ({0})
|
||||
sebserver.monitoring.exam.connection.action.hide.disabled=Hide Canceled ({0})
|
||||
sebserver.monitoring.exam.connection.action.show.disabled=Show Canceled ({0})
|
||||
sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined ({0})
|
||||
sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined ({0})
|
||||
sebserver.monitoring.exam.connection.action.hide.requested=Hide Requested ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.requested=Show Requested ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.active=Hide Active ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.active=Show Active ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.closed=Hide Closed ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.closed=Show Closed ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.disabled=Hide Canceled ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.disabled=Show Canceled ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.hide.undefined=Hide Undefined ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.show.undefined=Show Undefined ( {0} )
|
||||
sebserver.monitoring.exam.connection.action.proctoring=Single Room Proctoring
|
||||
sebserver.monitoring.exam.connection.action.proctoring.examroom=Exam Room Proctoring
|
||||
sebserver.monitoring.exam.connection.action.openTownhall.confirm=You are about to open the town-hall room and force all SEB clients to join the town-hall room.<br/>Are you sure to open the town-hall?
|
||||
|
|
Loading…
Reference in a new issue