SEBSERV-417 impl still in progress
This commit is contained in:
parent
a81447601b
commit
5cc7624f73
21 changed files with 458 additions and 313 deletions
|
@ -25,6 +25,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCac
|
|||
/** Concrete EntityDAO interface of Exam entities */
|
||||
public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSupportDAO<Exam> {
|
||||
|
||||
/** Use this to find an imported Exam with external id with like match
|
||||
*
|
||||
* @param internalQuizIdLike like match string that will be applied to like SQL match
|
||||
* @return Result refer to Exam that matches the query. If more then one matches, error is reported.*/
|
||||
Result<Exam> byExternalIdLike(String internalQuizIdLike);
|
||||
|
||||
/** Get a GrantEntity for the exam of specified id (PK)
|
||||
* This is actually a Exam instance but with no course data loaded.
|
||||
*
|
||||
|
@ -230,4 +236,6 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
|
|||
void markLMSAvailability(final String externalQuizId, final boolean available, final String updateId);
|
||||
|
||||
void updateQuitPassword(Exam exam, String quitPassword);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,10 +31,16 @@ public interface LmsSetupDAO extends ActivatableEntityDAO<LmsSetup, LmsSetup>, B
|
|||
Result<ProxyData> getLmsAPIAccessProxyData(String lmsSetupId);
|
||||
|
||||
/** Checks if the given LmsSetup instance is in sync with the version on
|
||||
* data base by matching the update_time field
|
||||
* database by matching the update_time field
|
||||
*
|
||||
* @param lmsSetup LmsSetup instance to check if it is up to date
|
||||
* @param lmsSetup LmsSetup instance to check if it is up-to-date
|
||||
* @return true if the update_time has the same value on persistent */
|
||||
boolean isUpToDate(LmsSetup lmsSetup);
|
||||
|
||||
/** This wil find the internal LMSSetup with the universal connectionId if it exists on this server.
|
||||
*
|
||||
* @param connectionId The connectionId UUID (unique ID)
|
||||
* @return the local LMSSetup DB PK ID */
|
||||
Result<Long> getLmsSetupIdByConnectionId(String connectionId);
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.function.Function;
|
|||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.*;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mybatis.dynamic.sql.update.UpdateDSL;
|
||||
|
@ -54,10 +55,6 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordMapper;
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
|
||||
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.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -93,6 +90,27 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
.flatMap(this::toDomainModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Exam> byExternalIdLike(final String internalQuizIdLike) {
|
||||
return Result.tryCatch(() -> {
|
||||
final List<ExamRecord> execute = examRecordMapper.selectByExample()
|
||||
.where(externalId, isLike(internalQuizIdLike))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
if (execute == null || execute.isEmpty()) {
|
||||
throw new NoResourceFoundException(EntityType.EXAM, "No exam found for external_id like" + internalQuizIdLike);
|
||||
}
|
||||
|
||||
if (execute.size() > 1) {
|
||||
throw new IllegalStateException("To many exams found for external_id like" + internalQuizIdLike);
|
||||
}
|
||||
|
||||
return execute.get(0);
|
||||
}).flatMap(this::toDomainModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<GrantEntity> examGrantEntityByPK(final Long id) {
|
||||
return this.examRecordDAO.recordById(id)
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.*;
|
|||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.*;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||
|
@ -41,11 +42,6 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDyn
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.LmsSetupRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
@ -75,6 +71,23 @@ public class LmsSetupDAOImpl implements LmsSetupDAO {
|
|||
.flatMap(this::toDomainModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Long> getLmsSetupIdByConnectionId(final String connectionId) {
|
||||
return Result.tryCatch(() -> {
|
||||
final List<Long> find = lmsSetupRecordMapper.selectIdsByExample()
|
||||
.where(LmsSetupRecordDynamicSqlSupport.connectionId, isEqualTo(connectionId))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
if (find == null || find.isEmpty()) {
|
||||
throw new NoResourceFoundException(EntityType.LMS_SETUP, "No LMSSetup with connection_id:" + connectionId);
|
||||
}
|
||||
|
||||
return find.get(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<LmsSetup>> all(final Long institutionId, final Boolean active) {
|
||||
|
|
|
@ -35,6 +35,8 @@ public interface ExamAdminService {
|
|||
|
||||
ProctoringAdminService getProctoringAdminService();
|
||||
|
||||
Result<Exam> applyPostCreationInitialization(Exam exam);
|
||||
|
||||
/** Get the exam domain object for the exam identifier (PK).
|
||||
*
|
||||
* @param examId the exam identifier
|
||||
|
@ -165,178 +167,6 @@ public interface ExamAdminService {
|
|||
|
||||
Result<Exam> applyQuitPassword(Exam exam);
|
||||
|
||||
static void newExamFieldValidation(final POSTMapper postParams) {
|
||||
noLMSFieldValidation(new Exam(postParams));
|
||||
}
|
||||
|
||||
static Exam noLMSFieldValidation(final Exam exam) {
|
||||
|
||||
// This only applies to exams that has no LMS
|
||||
if (exam.lmsSetupId != null) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
final Collection<APIMessage> validationErrors = new ArrayList<>();
|
||||
if (StringUtils.isBlank(exam.name)) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_NAME,
|
||||
"exam:quizName:notNull"));
|
||||
} else {
|
||||
final int length = exam.name.length();
|
||||
if (length < 3 || length > 255) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_NAME,
|
||||
"exam:quizName:size:3:255:" + length));
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(exam.getStartURL())) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
QuizData.QUIZ_ATTR_START_URL,
|
||||
"exam:quiz_start_url:notNull"));
|
||||
} else {
|
||||
try {
|
||||
new URL(exam.getStartURL()).toURI();
|
||||
} catch (final Exception e) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
QuizData.QUIZ_ATTR_START_URL,
|
||||
"exam:quiz_start_url:invalidURL"));
|
||||
}
|
||||
}
|
||||
|
||||
if (exam.startTime == null) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_START_TIME,
|
||||
"exam:quizStartTime:notNull"));
|
||||
} else if (exam.endTime != null) {
|
||||
if (exam.startTime
|
||||
.isAfter(exam.endTime)) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_END_TIME,
|
||||
"exam:quizEndTime:endBeforeStart"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
throw new APIMessageException(validationErrors);
|
||||
}
|
||||
|
||||
return exam;
|
||||
}
|
||||
|
||||
/** Used to check threshold consistency for a given list of thresholds.
|
||||
* Checks if all values are present (none null value)
|
||||
* Checks if there are duplicates
|
||||
* <p>
|
||||
* If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller
|
||||
*
|
||||
* @param thresholds List of Threshold */
|
||||
public static void checkThresholdConsistency(final List<Threshold> thresholds) {
|
||||
if (thresholds != null) {
|
||||
final List<Threshold> emptyThresholds = thresholds.stream()
|
||||
.filter(t -> t.getValue() == null || t.getColor() == null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!emptyThresholds.isEmpty()) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.EXAM.TYPE_NAME,
|
||||
Domain.EXAM.ATTR_SUPPORTER,
|
||||
"indicator:thresholds:thresholdEmpty")));
|
||||
}
|
||||
|
||||
final Set<Double> values = thresholds.stream()
|
||||
.map(t -> t.getValue())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (values.size() != thresholds.size()) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.EXAM.TYPE_NAME,
|
||||
Domain.EXAM.ATTR_SUPPORTER,
|
||||
"indicator:thresholds:thresholdDuplicate")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to check client group consistency for a given ClientGroup.
|
||||
* Checks if correct entries for specific type
|
||||
*
|
||||
* If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller
|
||||
*
|
||||
* @param clientGroup ClientGroup instance to check */
|
||||
public static <T extends ClientGroupData> T checkClientGroupConsistency(final T clientGroup) {
|
||||
final ClientGroupType type = clientGroup.getType();
|
||||
if (type == null || type == ClientGroupType.NONE) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
"clientGroup:type:notNull")));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case IP_V4_RANGE: {
|
||||
checkIPRange(clientGroup.getIpRangeStart(), clientGroup.getIpRangeEnd());
|
||||
break;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
checkClientOS(clientGroup.getClientOS());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
"clientGroup:type:typeInvalid")));
|
||||
}
|
||||
}
|
||||
|
||||
return clientGroup;
|
||||
}
|
||||
|
||||
static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) {
|
||||
final long startIP = Utils.ipToLong(ipRangeStart);
|
||||
if (StringUtils.isBlank(ipRangeStart) || startIP < 0) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIP")));
|
||||
}
|
||||
final long endIP = Utils.ipToLong(ipRangeEnd);
|
||||
if (StringUtils.isBlank(ipRangeEnd) || endIP < 0) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIP")));
|
||||
}
|
||||
|
||||
if (endIP <= startIP) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIPRange")),
|
||||
APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIPRange")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void checkClientOS(final ClientOS clientOS) {
|
||||
if (clientOS == null) {
|
||||
throw new APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroupData.ATTR_CLIENT_OS,
|
||||
"clientGroup:clientOS:notNull")));
|
||||
}
|
||||
}
|
||||
Result<Exam> findExamByLmsIdentity(String courseId, String quizId, String identity);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, IT Services
|
||||
*
|
||||
* 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.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.*;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.validation.FieldError;
|
||||
|
||||
public final class ExamUtils {
|
||||
|
||||
public static void newExamFieldValidation(final POSTMapper postParams) {
|
||||
noLMSFieldValidation(new Exam(postParams));
|
||||
}
|
||||
|
||||
public static Exam noLMSFieldValidation(final Exam exam) {
|
||||
|
||||
// This only applies to exams that has no LMS
|
||||
if (exam.lmsSetupId != null) {
|
||||
return exam;
|
||||
}
|
||||
|
||||
final Collection<APIMessage> validationErrors = new ArrayList<>();
|
||||
if (StringUtils.isBlank(exam.name)) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_NAME,
|
||||
"exam:quizName:notNull"));
|
||||
} else {
|
||||
final int length = exam.name.length();
|
||||
if (length < 3 || length > 255) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_NAME,
|
||||
"exam:quizName:size:3:255:" + length));
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(exam.getStartURL())) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
QuizData.QUIZ_ATTR_START_URL,
|
||||
"exam:quiz_start_url:notNull"));
|
||||
} else {
|
||||
try {
|
||||
new URL(exam.getStartURL()).toURI();
|
||||
} catch (final Exception e) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
QuizData.QUIZ_ATTR_START_URL,
|
||||
"exam:quiz_start_url:invalidURL"));
|
||||
}
|
||||
}
|
||||
|
||||
if (exam.startTime == null) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_START_TIME,
|
||||
"exam:quizStartTime:notNull"));
|
||||
} else if (exam.endTime != null) {
|
||||
if (exam.startTime
|
||||
.isAfter(exam.endTime)) {
|
||||
validationErrors.add(APIMessage.fieldValidationError(
|
||||
Domain.EXAM.ATTR_QUIZ_END_TIME,
|
||||
"exam:quizEndTime:endBeforeStart"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
throw new APIMessage.APIMessageException(validationErrors);
|
||||
}
|
||||
|
||||
return exam;
|
||||
}
|
||||
|
||||
/** Used to check threshold consistency for a given list of thresholds.
|
||||
* Checks if all values are present (none null value)
|
||||
* Checks if there are duplicates
|
||||
* <p>
|
||||
* If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller
|
||||
*
|
||||
* @param thresholds List of Threshold */
|
||||
public static void checkThresholdConsistency(final List<Indicator.Threshold> thresholds) {
|
||||
if (thresholds != null) {
|
||||
final List<Indicator.Threshold> emptyThresholds = thresholds.stream()
|
||||
.filter(t -> t.getValue() == null || t.getColor() == null)
|
||||
.toList();
|
||||
|
||||
if (!emptyThresholds.isEmpty()) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.EXAM.TYPE_NAME,
|
||||
Domain.EXAM.ATTR_SUPPORTER,
|
||||
"indicator:thresholds:thresholdEmpty")));
|
||||
}
|
||||
|
||||
final Set<Double> values = thresholds.stream()
|
||||
.map(Indicator.Threshold::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (values.size() != thresholds.size()) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.EXAM.TYPE_NAME,
|
||||
Domain.EXAM.ATTR_SUPPORTER,
|
||||
"indicator:thresholds:thresholdDuplicate")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to check client group consistency for a given ClientGroup.
|
||||
* Checks if correct entries for specific type
|
||||
* <p>
|
||||
* If a check fails, the methods throws a APIMessageException with a FieldError to notify the caller
|
||||
*
|
||||
* @param clientGroup ClientGroup instance to check */
|
||||
public static <T extends ClientGroupData> T checkClientGroupConsistency(final T clientGroup) {
|
||||
final ClientGroupData.ClientGroupType type = clientGroup.getType();
|
||||
if (type == null || type == ClientGroupData.ClientGroupType.NONE) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
"clientGroup:type:notNull")));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case IP_V4_RANGE: {
|
||||
checkIPRange(clientGroup.getIpRangeStart(), clientGroup.getIpRangeEnd());
|
||||
break;
|
||||
}
|
||||
case CLIENT_OS: {
|
||||
checkClientOS(clientGroup.getClientOS());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
Domain.CLIENT_GROUP.ATTR_TYPE,
|
||||
"clientGroup:type:typeInvalid")));
|
||||
}
|
||||
}
|
||||
|
||||
return clientGroup;
|
||||
}
|
||||
|
||||
static void checkIPRange(final String ipRangeStart, final String ipRangeEnd) {
|
||||
final long startIP = Utils.ipToLong(ipRangeStart);
|
||||
if (StringUtils.isBlank(ipRangeStart) || startIP < 0) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIP")));
|
||||
}
|
||||
final long endIP = Utils.ipToLong(ipRangeEnd);
|
||||
if (StringUtils.isBlank(ipRangeEnd) || endIP < 0) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIP")));
|
||||
}
|
||||
|
||||
if (endIP <= startIP) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_START,
|
||||
"clientGroup:ipRangeStart:invalidIPRange")),
|
||||
APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroup.ATTR_IP_RANGE_END,
|
||||
"clientGroup:ipRangeEnd:invalidIPRange")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void checkClientOS(final ClientGroupData.ClientOS clientOS) {
|
||||
if (clientOS == null) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.fieldValidationError(
|
||||
new FieldError(
|
||||
Domain.CLIENT_GROUP.TYPE_NAME,
|
||||
ClientGroupData.ATTR_CLIENT_OS,
|
||||
"clientGroup:clientOS:notNull")));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,10 +9,14 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.*;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateService;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
@ -41,16 +45,13 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.Configuration
|
|||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationNodeDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamConfigurationMapDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService;
|
||||
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.lms.impl.moodle.MoodleUtils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.RemoteProctoringService;
|
||||
|
||||
@Lazy
|
||||
|
@ -71,6 +72,8 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
private final ExamConfigurationValueService examConfigurationValueService;
|
||||
private final SEBRestrictionService sebRestrictionService;
|
||||
|
||||
private final ExamTemplateService examTemplateService;
|
||||
|
||||
protected ExamAdminServiceImpl(
|
||||
final ExamDAO examDAO,
|
||||
final ProctoringAdminService proctoringAdminService,
|
||||
|
@ -80,6 +83,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
final LmsAPIService lmsAPIService,
|
||||
final ExamConfigurationValueService examConfigurationValueService,
|
||||
final SEBRestrictionService sebRestrictionService,
|
||||
final ExamTemplateService examTemplateService,
|
||||
final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.enabled:false}") boolean appSignatureKeyEnabled,
|
||||
final @Value("${sebserver.webservice.api.admin.exam.app.signature.key.numerical.threshold:2}") int defaultNumericalTrustThreshold) {
|
||||
|
||||
|
@ -93,6 +97,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
this.appSignatureKeyEnabled = appSignatureKeyEnabled;
|
||||
this.defaultNumericalTrustThreshold = defaultNumericalTrustThreshold;
|
||||
this.sebRestrictionService = sebRestrictionService;
|
||||
this.examTemplateService = examTemplateService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,6 +105,60 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
return this.proctoringAdminService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> applyPostCreationInitialization(final Exam exam) {
|
||||
final List<APIMessage> errors = new ArrayList<>();
|
||||
|
||||
this.initAdditionalAttributes(exam)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES.of(error));
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this.examTemplateService::addDefinedIndicators)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_INDICATOR.of(error));
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this.examTemplateService::addDefinedClientGroups)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CLIENT_GROUPS.of(error));
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initAdditionalTemplateAttributes)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES.of(error));
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initExamConfiguration)
|
||||
.onErrorDo(error -> {
|
||||
if (error instanceof APIMessageException) {
|
||||
errors.addAll(((APIMessageException) error).getAPIMessages());
|
||||
} else {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CONFIG.of(error));
|
||||
}
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this::applyAdditionalSEBRestrictions)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_RESTRICTION.of(error));
|
||||
return exam;
|
||||
});
|
||||
|
||||
this.applyQuitPassword(exam);
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
errors.add(0, APIMessage.ErrorMessage.EXAM_IMPORT_ERROR_AUTO_SETUP.of(
|
||||
exam.getModelId(),
|
||||
API.PARAM_MODEL_ID + Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR + exam.getModelId()));
|
||||
|
||||
log.warn("Exam successfully created but some initialization did go wrong: {}", errors);
|
||||
|
||||
throw new APIMessageException(errors);
|
||||
} else {
|
||||
return this.examDAO.byPK(exam.id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> examForPK(final Long examId) {
|
||||
return this.examDAO.byPK(examId);
|
||||
|
@ -343,6 +402,35 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
.onError(t -> log.error("Failed to update SEB Client restriction for Exam: {}", exam, t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> findExamByLmsIdentity(
|
||||
final String courseId,
|
||||
final String quizId,
|
||||
final String identity) {
|
||||
|
||||
for (final LmsType lmsType : LmsType.values()) {
|
||||
switch (lmsType) {
|
||||
case MOODLE_PLUGIN -> {
|
||||
if (StringUtils.isBlank(quizId) || StringUtils.isBlank(courseId)) {
|
||||
return Result.ofError(new APIMessageException(
|
||||
APIMessage.ErrorMessage.FIELD_VALIDATION.of("Missing courseId or quizId")));
|
||||
}
|
||||
|
||||
return examDAO.byExternalIdLike(MoodleUtils.getInternalQuizId(
|
||||
quizId,
|
||||
courseId,
|
||||
Constants.PERCENTAGE_STRING,
|
||||
Constants.PERCENTAGE_STRING));
|
||||
}
|
||||
// TODO add other LMS types if they support full integration
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ofError(
|
||||
new ResourceNotFoundException(EntityType.EXAM,
|
||||
"Not found by LMS identity [" + courseId + "|"+ quizId+ "|"+ identity + "]"));
|
||||
}
|
||||
|
||||
private Result<Exam> initAdditionalAttributesForMoodleExams(final Exam exam) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
|
|
|
@ -12,9 +12,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
|
||||
public interface FullLmsIntegrationAPI {
|
||||
|
||||
Result<Void> createConnectionDetails();
|
||||
|
||||
Result<Void> updateConnectionDetails();
|
||||
Result<Void> applyConnectionDetails(FullLmsIntegrationService.IntegrationData data);
|
||||
|
||||
Result<Void> deleteConnectionDetails();
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@ import java.util.Map;
|
|||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public interface FullLmsIntegrationService {
|
||||
|
||||
Result<LmsAPITemplate> getLmsAPITemplate(String lmsUUID);
|
||||
|
||||
Result<Void> refreshAccessToken(String lmsUUID);
|
||||
|
||||
Result<Void> applyFullLmsIntegration(Long lmsSetupId);
|
||||
|
@ -45,4 +45,32 @@ public interface FullLmsIntegrationService {
|
|||
String courseId,
|
||||
String quizId,
|
||||
OutputStream out);
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
final class IntegrationData {
|
||||
@JsonProperty("id")
|
||||
public final String id;
|
||||
@JsonProperty("name")
|
||||
public final String name;
|
||||
@JsonProperty("url")
|
||||
public final String url;
|
||||
@JsonProperty("access_token")
|
||||
public final String access_token;
|
||||
@JsonProperty("exam_templates")
|
||||
public final Map<String, String> exam_templates;
|
||||
|
||||
public IntegrationData(
|
||||
@JsonProperty("id") final String id,
|
||||
@JsonProperty("name") final String name,
|
||||
@JsonProperty("url") final String url,
|
||||
@JsonProperty("access_token") final String access_token,
|
||||
@JsonProperty("exam_templates") final Map<String, String> exam_templates) {
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.access_token = access_token;
|
||||
this.exam_templates = exam_templates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,6 @@ public interface QuizLookupService {
|
|||
* @param sortAttribute the sort attribute for the new Page
|
||||
* @param pageNumber the number of the Page to build
|
||||
* @param pageSize the size of the Page to build
|
||||
* @param complete indicates if the quiz lookup that uses this page function has been completed yet
|
||||
* @return A Page of QuizData extracted from a given list of QuizData */
|
||||
static Function<LookupResult, Page<QuizData>> quizzesToPageFunction(
|
||||
final String sortAttribute,
|
||||
|
|
|
@ -10,13 +10,16 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
|||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -25,9 +28,16 @@ import org.springframework.stereotype.Service;
|
|||
@Service
|
||||
@WebServiceProfile
|
||||
public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService {
|
||||
@Override
|
||||
public Result<LmsAPITemplate> getLmsAPITemplate(final String lmsUUID) {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
|
||||
private final LmsSetupDAO lmsSetupDAO;
|
||||
private final LmsAPIService lmsAPIService;
|
||||
|
||||
public FullLmsIntegrationServiceImpl(
|
||||
final LmsSetupDAO lmsSetupDAO,
|
||||
final LmsAPIService lmsAPIService) {
|
||||
|
||||
this.lmsSetupDAO = lmsSetupDAO;
|
||||
this.lmsAPIService = lmsAPIService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,9 +68,16 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
final String examTemplateId,
|
||||
final String quitPassword,
|
||||
final String quitLink) {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
|
||||
return lmsSetupDAO.getLmsSetupIdByConnectionId(lmsUUID)
|
||||
.flatMap(lmsAPIService::getLmsAPITemplate)
|
||||
.map(findQuizData(courseId, quizId))
|
||||
.map(createExam(examTemplateId, quitPassword, quitLink));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result<EntityKey> deleteExam(
|
||||
final String lmsUUID,
|
||||
|
@ -78,8 +95,30 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
private Long findLMSSetup(final String lmsUUID) {
|
||||
// TODO
|
||||
return null;
|
||||
private Function<LmsAPITemplate, QuizData> findQuizData(
|
||||
final String courseId,
|
||||
final String quizId) {
|
||||
|
||||
return LmsAPITemplate -> {
|
||||
// TODO find quiz data for quizId and courseId on LMS
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Function<QuizData, Exam> createExam(
|
||||
final String examTemplateId,
|
||||
final String quitPassword,
|
||||
final String quitLink) {
|
||||
|
||||
return quizData -> {
|
||||
// TODO create and store Exam with DAO and apply all post processing needed for import
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
private Exam createAdHocSupporterAccount(Exam exam) {
|
||||
// TODO create an ad hoc supporter account for this exam and apply it to the exam
|
||||
return exam;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -462,7 +462,7 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> createConnectionDetails() {
|
||||
public Result<Void> applyConnectionDetails(final FullLmsIntegrationService.IntegrationData data) {
|
||||
if (this.lmsIntegrationAPI == null) {
|
||||
return Result.ofError(
|
||||
new UnsupportedOperationException("LMS Integration API Not Supported For: " + getType().name()));
|
||||
|
@ -472,31 +472,13 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
|||
log.debug("Create LMS connection details for LMSSetup: {}", lmsSetup());
|
||||
}
|
||||
|
||||
return this.lmsAccessRequest.protectedRun(() -> this.lmsIntegrationAPI.createConnectionDetails()
|
||||
return this.lmsAccessRequest.protectedRun(() -> this.lmsIntegrationAPI.applyConnectionDetails(data)
|
||||
.onError(error -> log.error(
|
||||
"Failed to run protected createConnectionDetails: {}",
|
||||
error.getMessage()))
|
||||
.getOrThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> updateConnectionDetails() {
|
||||
if (this.lmsIntegrationAPI == null) {
|
||||
return Result.ofError(
|
||||
new UnsupportedOperationException("LMS Integration API Not Supported For: " + getType().name()));
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Update LMS connection details for LMSSetup: {}", lmsSetup());
|
||||
}
|
||||
|
||||
return this.lmsAccessRequest.protectedRun(() -> this.lmsIntegrationAPI.updateConnectionDetails()
|
||||
.onError(error -> log.error(
|
||||
"Failed to run protected updateConnectionDetails: {}",
|
||||
error.getMessage()))
|
||||
.getOrThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> deleteConnectionDetails() {
|
||||
if (this.lmsIntegrationAPI == null) {
|
||||
|
@ -514,4 +496,5 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
|||
error.getMessage()))
|
||||
.getOrThrow());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
@ -421,12 +422,7 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> createConnectionDetails() {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> updateConnectionDetails() {
|
||||
public Result<Void> applyConnectionDetails(final FullLmsIntegrationService.IntegrationData data) {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
|
|
|
@ -10,17 +10,13 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationAPI;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
|
||||
|
||||
public class MockupFullIntegration implements FullLmsIntegrationAPI {
|
||||
|
||||
|
||||
@Override
|
||||
public Result<Void> createConnectionDetails() {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> updateConnectionDetails() {
|
||||
public Result<Void> applyConnectionDetails(FullLmsIntegrationService.IntegrationData data) {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
|||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationAPI;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
|
||||
public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
|
||||
|
@ -27,12 +28,7 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> createConnectionDetails() {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> updateConnectionDetails() {
|
||||
public Result<Void> applyConnectionDetails(FullLmsIntegrationService.IntegrationData data) {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -408,12 +409,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> createConnectionDetails() {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Void> updateConnectionDetails() {
|
||||
public Result<Void> applyConnectionDetails(final FullLmsIntegrationService.IntegrationData data) {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
@ -90,13 +91,13 @@ public class ClientGroupController extends EntityController<ClientGroup, ClientG
|
|||
@Override
|
||||
protected Result<ClientGroup> validForCreate(final ClientGroup entity) {
|
||||
return super.validForCreate(entity)
|
||||
.map(ExamAdminService::checkClientGroupConsistency);
|
||||
.map(ExamUtils::checkClientGroupConsistency);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Result<ClientGroup> validForSave(final ClientGroup entity) {
|
||||
return super.validForSave(entity)
|
||||
.map(ExamAdminService::checkClientGroupConsistency);
|
||||
.map(ExamUtils::checkClientGroupConsistency);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.stream.Collectors;
|
|||
import javax.validation.Valid;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -34,7 +35,6 @@ 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.model.Domain;
|
||||
|
@ -67,7 +67,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamTemplateService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
|
||||
|
@ -88,7 +87,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
private final UserDAO userDAO;
|
||||
private final ExamAdminService examAdminService;
|
||||
private final RemoteProctoringRoomService remoteProctoringRoomService;
|
||||
private final ExamTemplateService examTemplateService;
|
||||
private final LmsAPIService lmsAPIService;
|
||||
private final ExamSessionService examSessionService;
|
||||
private final SEBRestrictionService sebRestrictionService;
|
||||
|
@ -106,7 +104,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
final UserDAO userDAO,
|
||||
final ExamAdminService examAdminService,
|
||||
final RemoteProctoringRoomService remoteProctoringRoomService,
|
||||
final ExamTemplateService examTemplateService,
|
||||
final ExamSessionService examSessionService,
|
||||
final SEBRestrictionService sebRestrictionService,
|
||||
final SecurityKeyService securityKeyService,
|
||||
|
@ -123,7 +120,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
this.userDAO = userDAO;
|
||||
this.examAdminService = examAdminService;
|
||||
this.remoteProctoringRoomService = remoteProctoringRoomService;
|
||||
this.examTemplateService = examTemplateService;
|
||||
this.lmsAPIService = lmsAPIService;
|
||||
this.examSessionService = examSessionService;
|
||||
this.sebRestrictionService = sebRestrictionService;
|
||||
|
@ -596,7 +592,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
|
||||
// NO LMS based exam is possible since v1.6
|
||||
if (quizId == null) {
|
||||
ExamAdminService.newExamFieldValidation(postParams);
|
||||
ExamUtils.newExamFieldValidation(postParams);
|
||||
return new Exam(postParams);
|
||||
} else {
|
||||
return this.lmsAPIService
|
||||
|
@ -613,57 +609,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
|
||||
@Override
|
||||
protected Result<Exam> notifyCreated(final Exam entity) {
|
||||
final List<APIMessage> errors = new ArrayList<>();
|
||||
|
||||
this.examAdminService
|
||||
.initAdditionalAttributes(entity)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::addDefinedIndicators)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_INDICATOR.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::addDefinedClientGroups)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_CLIENT_GROUPS.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initAdditionalTemplateAttributes)
|
||||
.onErrorDo(error -> {
|
||||
errors.add(ErrorMessage.EXAM_IMPORT_ERROR_AUTO_ATTRIBUTES.of(error));
|
||||
return entity;
|
||||
})
|
||||
.flatMap(this.examTemplateService::initExamConfiguration)
|
||||
.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;
|
||||
});
|
||||
|
||||
this.examAdminService.applyQuitPassword(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()));
|
||||
|
||||
log.warn("Exam successfully created but some initialization did go wrong: {}", errors);
|
||||
|
||||
throw new APIMessageException(errors);
|
||||
} else {
|
||||
return this.examDAO.byPK(entity.id);
|
||||
}
|
||||
return examAdminService.applyPostCreationInitialization(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -683,7 +629,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
protected Result<Exam> validForSave(final Exam entity) {
|
||||
return super.validForSave(entity)
|
||||
.map(this::checkExamSupporterRole)
|
||||
.map(ExamAdminService::noLMSFieldValidation)
|
||||
.map(ExamUtils::noLMSFieldValidation)
|
||||
.map(this::checkQuitPasswordChange);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.validation.Valid;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -381,7 +382,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
null,
|
||||
postMap.getLong(ClientGroupTemplate.ATTR_EXAM_TEMPLATE_ID),
|
||||
postMap))
|
||||
.map(ExamAdminService::checkClientGroupConsistency)
|
||||
.map(ExamUtils::checkClientGroupConsistency)
|
||||
.flatMap(this.examTemplateDAO::createNewClientGroupTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logCreate)
|
||||
.getOrThrow();
|
||||
|
@ -403,7 +404,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
this.checkModifyPrivilege(institutionId);
|
||||
return this.beanValidationService
|
||||
.validateBean(modifyData)
|
||||
.map(ExamAdminService::checkClientGroupConsistency)
|
||||
.map(ExamUtils::checkClientGroupConsistency)
|
||||
.flatMap(this.examTemplateDAO::saveClientGroupTemplate)
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.getOrThrow();
|
||||
|
@ -547,7 +548,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
}
|
||||
|
||||
private IndicatorTemplate checkIndicatorConsistency(final IndicatorTemplate indicatorTemplate) {
|
||||
ExamAdminService.checkThresholdConsistency(indicatorTemplate.thresholds);
|
||||
ExamUtils.checkThresholdConsistency(indicatorTemplate.thresholds);
|
||||
return indicatorTemplate;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamUtils;
|
||||
import org.mybatis.dynamic.sql.SqlTable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
@ -90,7 +91,7 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
|
|||
protected Result<Indicator> validForCreate(final Indicator entity) {
|
||||
return super.validForCreate(entity)
|
||||
.map(indicator -> {
|
||||
ExamAdminService.checkThresholdConsistency(indicator.thresholds);
|
||||
ExamUtils.checkThresholdConsistency(indicator.thresholds);
|
||||
return indicator;
|
||||
});
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ public class IndicatorController extends EntityController<Indicator, Indicator>
|
|||
protected Result<Indicator> validForSave(final Indicator entity) {
|
||||
return super.validForSave(entity)
|
||||
.map(indicator -> {
|
||||
ExamAdminService.checkThresholdConsistency(indicator.thresholds);
|
||||
ExamUtils.checkThresholdConsistency(indicator.thresholds);
|
||||
return indicator;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ public class QuizController {
|
|||
|
||||
/** This is called by Spring to initialize the WebDataBinder and is used here to
|
||||
* initialize the default value binding for the institutionId request-parameter
|
||||
* that has the current users insitutionId as default.
|
||||
*
|
||||
* that has the current users institutionId as default.
|
||||
* <p>
|
||||
* See also UserService.addUsersInstitutionDefaultPropertySupport */
|
||||
@InitBinder
|
||||
public void initBinder(final WebDataBinder binder) {
|
||||
|
|
Loading…
Reference in a new issue