SEBSERV-417 create delete exam from Moodle
This commit is contained in:
parent
c0919ce0cf
commit
ff89864b19
14 changed files with 247 additions and 82 deletions
|
@ -18,6 +18,8 @@ import java.util.Map;
|
|||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -415,6 +417,31 @@ public final class Exam implements GrantEntity {
|
|||
return this.additionalAttributes.get(attrName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exam printSecureCopy() {
|
||||
return new Exam(
|
||||
id,
|
||||
institutionId,
|
||||
lmsSetupId,
|
||||
externalId,
|
||||
lmsAvailable,
|
||||
name,
|
||||
startTime,
|
||||
endTime,
|
||||
type,
|
||||
owner,
|
||||
supporter,
|
||||
status,
|
||||
"--",
|
||||
sebRestriction,
|
||||
"--",
|
||||
active,
|
||||
lastUpdate,
|
||||
examTemplateId,
|
||||
lastModified,
|
||||
Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
|
|
@ -33,7 +33,8 @@ public final class LmsSetupTestResult {
|
|||
TOKEN_REQUEST,
|
||||
QUIZ_ACCESS_API_REQUEST,
|
||||
QUIZ_RESTRICTION_API_REQUEST,
|
||||
TEMPLATE_CREATION
|
||||
TEMPLATE_CREATION,
|
||||
APPLY_FULL_INTEGRATION,
|
||||
}
|
||||
|
||||
@JsonProperty(Domain.LMS_SETUP.ATTR_LMS_TYPE)
|
||||
|
@ -138,6 +139,10 @@ public final class LmsSetupTestResult {
|
|||
return new LmsSetupTestResult(lmsType, new Error(ErrorType.QUIZ_RESTRICTION_API_REQUEST, message));
|
||||
}
|
||||
|
||||
public static LmsSetupTestResult ofFullIntegrationAPIError(final LmsSetup.LmsType lmsType, final String message) {
|
||||
return new LmsSetupTestResult(lmsType, new Error(ErrorType.APPLY_FULL_INTEGRATION, message));
|
||||
}
|
||||
|
||||
public final static class Error {
|
||||
|
||||
@JsonProperty(ATTR_ERROR_TYPE)
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.stream.Stream;
|
|||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.user.UserFeatures;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.*;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.GetLmsSetup;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
|
@ -204,7 +205,7 @@ public class ExamForm implements TemplateComposer {
|
|||
final boolean editable = modifyGrant && (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.RUNNING);
|
||||
final boolean signatureKeyCheckEnabled = BooleanUtils.toBoolean(
|
||||
exam.additionalAttributes.get(Exam.ADDITIONAL_ATTR_SIGNATURE_KEY_CHECK_ENABLED));
|
||||
final boolean sebRestrictionAvailable = readonly && testSEBRestrictionAPI(exam);
|
||||
final boolean sebRestrictionAvailable = readonly && hasSEBRestrictionAPI(exam);
|
||||
final boolean isRestricted = readonly && sebRestrictionAvailable && this.restService
|
||||
.getBuilder(CheckSEBRestriction.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
|
||||
|
@ -742,25 +743,22 @@ public class ExamForm implements TemplateComposer {
|
|||
throw new RuntimeException("Error while handle exam import setup failure:", e);
|
||||
}
|
||||
|
||||
private boolean testSEBRestrictionAPI(final Exam exam) {
|
||||
private boolean hasSEBRestrictionAPI(final Exam exam) {
|
||||
if (exam.lmsSetupId == null || !exam.isLmsAvailable() || exam.status == ExamStatus.ARCHIVED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call the testing endpoint with the specified data to test
|
||||
final Result<LmsSetupTestResult> result = this.restService.getBuilder(TestLmsSetup.class)
|
||||
// get LMSSetup
|
||||
final Result<LmsSetup> call = this.restService.getBuilder(GetLmsSetup.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, String.valueOf(exam.lmsSetupId))
|
||||
.call();
|
||||
|
||||
if (result.hasError()) {
|
||||
if (call.hasError()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final LmsSetupTestResult lmsSetupTestResult = result.get();
|
||||
if (!lmsSetupTestResult.lmsType.features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
|
||||
return false;
|
||||
}
|
||||
return !lmsSetupTestResult.hasError(ErrorType.QUIZ_RESTRICTION_API_REQUEST);
|
||||
final LmsSetup lmsSetup = call.get();
|
||||
return (lmsSetup.getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION));
|
||||
}
|
||||
|
||||
private void showConsistencyChecks(
|
||||
|
|
|
@ -90,7 +90,7 @@ import ch.ethz.seb.sebserver.gui.service.session.MonitoringEntry;
|
|||
@GuiProfile
|
||||
/** Defines functionality to get resources or functions of resources to feed e.g. selection or
|
||||
* combo-box content.
|
||||
* */
|
||||
*/
|
||||
public class ResourceService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ResourceService.class);
|
||||
|
@ -653,7 +653,7 @@ public class ResourceService {
|
|||
}
|
||||
|
||||
public String localizedClientConnectionStatusName(final ConnectionStatus status) {
|
||||
String name;
|
||||
final String name;
|
||||
if (status != null) {
|
||||
name = status.name();
|
||||
} else {
|
||||
|
|
|
@ -116,6 +116,10 @@ public class UserServiceImpl implements UserService {
|
|||
public SEBServerUser extract(final Principal principal) {
|
||||
if (principal instanceof OAuth2Authentication) {
|
||||
final Authentication userAuthentication = ((OAuth2Authentication) principal).getUserAuthentication();
|
||||
if (userAuthentication == null) {
|
||||
// check if lms integration client
|
||||
return isLMSIntegrationClient(principal);
|
||||
}
|
||||
if (userAuthentication instanceof UsernamePasswordAuthenticationToken) {
|
||||
final Object userPrincipal = userAuthentication.getPrincipal();
|
||||
if (userPrincipal instanceof SEBServerUser) {
|
||||
|
@ -128,6 +132,26 @@ public class UserServiceImpl implements UserService {
|
|||
}
|
||||
}
|
||||
|
||||
private static SEBServerUser isLMSIntegrationClient(final Principal principal) {
|
||||
final String name = principal.getName();
|
||||
if ("lmsClient".equals(name)) {
|
||||
return new SEBServerUser(
|
||||
-1L,
|
||||
new UserInfo("LMS_INTEGRATION_CLIENT", -1L, null, "lmsIntegrationClient", "lmsIntegrationClient", "lmsIntegrationClient", null,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
null, null,
|
||||
Arrays.stream(UserRole.values())
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toSet()),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList()),
|
||||
null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. Separated thread strategy
|
||||
@Lazy
|
||||
@Component
|
||||
|
|
|
@ -99,6 +99,14 @@ public class DeleteExamAction implements BatchActionExec {
|
|||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Result<EntityKey> deleteExamFromLMSIntegration(final Exam exam) {
|
||||
return deleteExamDependencies(exam)
|
||||
.flatMap(this::deleteExamWithRefs)
|
||||
.map(Exam::getEntityKey)
|
||||
.onError(TransactionHandler::rollback);
|
||||
}
|
||||
|
||||
private Result<Exam> deleteExamDependencies(final Exam entity) {
|
||||
return this.clientConnectionDAO.deleteAllForExam(entity.id)
|
||||
.map(this::logDelete)
|
||||
|
|
|
@ -126,10 +126,14 @@ public class ExamRecordDAO {
|
|||
@Transactional(readOnly = true)
|
||||
public Result<Collection<Long>> allInstitutionIdsByQuizId(final String quizId) {
|
||||
return Result.tryCatch(() -> {
|
||||
if (StringUtils.isBlank(quizId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return this.examRecordMapper.selectByExample()
|
||||
.where(
|
||||
ExamRecordDynamicSqlSupport.externalId,
|
||||
isEqualToWhenPresent(quizId))
|
||||
isEqualTo(quizId))
|
||||
.and(
|
||||
ExamRecordDynamicSqlSupport.active,
|
||||
isEqualToWhenPresent(BooleanUtils.toIntegerObject(true)))
|
||||
|
|
|
@ -150,6 +150,4 @@ public interface ExamAdminService {
|
|||
|
||||
Result<Exam> applyQuitPassword(Exam exam);
|
||||
|
||||
Result<Exam> findExamByLmsIdentity(String courseId, String quizId, String identity);
|
||||
|
||||
}
|
||||
|
|
|
@ -385,37 +385,6 @@ 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(() -> {
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public interface LmsAPIService {
|
|||
* @return LmsAPITemplate for specified LmsSetup configuration */
|
||||
Result<LmsAPITemplate> getLmsAPITemplate(String lmsSetupId);
|
||||
|
||||
/** use this to the the specified LmsAPITemplate.
|
||||
/** use this to the specified LmsAPITemplate.
|
||||
*
|
||||
* @param template the LmsAPITemplate
|
||||
* @return LmsSetupTestResult containing list of errors if happened */
|
||||
|
|
|
@ -10,15 +10,14 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
|||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
|
||||
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.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
|
@ -26,12 +25,14 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
|
||||
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.sebconfig.SEBClientConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.LmsSetupDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.impl.SEBServerUser;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.DeleteExamAction;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.*;
|
||||
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.ExamTemplateChangeEvent;
|
||||
|
@ -39,7 +40,10 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.FullLmsIntegrationServi
|
|||
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.impl.moodle.MoodleUtils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
@ -57,33 +61,52 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
private static final Logger log = LoggerFactory.getLogger(FullLmsIntegrationServiceImpl.class);
|
||||
|
||||
private final LmsSetupDAO lmsSetupDAO;
|
||||
private final UserActivityLogDAO userActivityLogDAO;
|
||||
private final SEBClientConfigDAO sebClientConfigDAO;
|
||||
private final ClientConfigService clientConfigService;
|
||||
private final DeleteExamAction deleteExamAction;
|
||||
private final LmsAPIService lmsAPIService;
|
||||
private final ExamAdminService examAdminService;
|
||||
private final ExamSessionService examSessionService;
|
||||
private final ExamDAO examDAO;
|
||||
private final ExamTemplateDAO examTemplateDAO;
|
||||
private final WebserviceInfo webserviceInfo;
|
||||
|
||||
private final String lmsAPIEndpoint;
|
||||
private final UserService userService;
|
||||
private final ClientCredentialsResourceDetails resource;
|
||||
|
||||
private final OAuth2RestTemplate restTemplate;
|
||||
|
||||
public FullLmsIntegrationServiceImpl(
|
||||
final LmsSetupDAO lmsSetupDAO,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final SEBClientConfigDAO sebClientConfigDAO,
|
||||
final ClientConfigService clientConfigService,
|
||||
final DeleteExamAction deleteExamAction,
|
||||
final LmsAPIService lmsAPIService,
|
||||
final ExamAdminService examAdminService,
|
||||
final ExamSessionService examSessionService,
|
||||
final ExamDAO examDAO,
|
||||
final ExamTemplateDAO examTemplateDAO,
|
||||
final WebserviceInfo webserviceInfo,
|
||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
||||
final UserService userService,
|
||||
@Value("${sebserver.webservice.lms.api.endpoint}") final String lmsAPIEndpoint,
|
||||
@Value("${sebserver.webservice.lms.api.clientId}") final String clientId,
|
||||
@Value("${sebserver.webservice.api.admin.clientSecret}") final String clientSecret) {
|
||||
|
||||
this.lmsSetupDAO = lmsSetupDAO;
|
||||
this.userActivityLogDAO = userActivityLogDAO;
|
||||
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||
this.clientConfigService = clientConfigService;
|
||||
this.deleteExamAction = deleteExamAction;
|
||||
this.lmsAPIService = lmsAPIService;
|
||||
this.examAdminService = examAdminService;
|
||||
this.examSessionService = examSessionService;
|
||||
this.examDAO = examDAO;
|
||||
this.examTemplateDAO = examTemplateDAO;
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.lmsAPIEndpoint = lmsAPIEndpoint;
|
||||
this.userService = userService;
|
||||
|
||||
resource = new ClientCredentialsResourceDetails();
|
||||
resource.setAccessTokenUri(webserviceInfo.getOAuthTokenURI());
|
||||
|
@ -163,7 +186,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
final IntegrationData data = new IntegrationData(
|
||||
connectionId,
|
||||
lmsSetup.name,
|
||||
webserviceInfo.getExternalServerURL(),
|
||||
getAPIRootURL(),
|
||||
accessToken,
|
||||
this.getIntegrationTemplates(lmsSetup.institutionId)
|
||||
);
|
||||
|
@ -181,6 +204,8 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Result<Boolean> deleteFullLmsIntegration(final Long lmsSetupId) {
|
||||
return lmsSetupDAO
|
||||
|
@ -219,8 +244,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
.getLmsSetupIdByConnectionId(lmsUUID)
|
||||
.flatMap(lmsAPIService::getLmsAPITemplate)
|
||||
.map(findQuizData(courseId, quizId))
|
||||
.map(createExam(examTemplateId, quitPassword))
|
||||
.map(this::createAdHocSupporterAccount);
|
||||
.map(createAccountAndExam(examTemplateId, quitPassword));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -229,8 +253,27 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
final String courseId,
|
||||
final String quizId) {
|
||||
|
||||
return findExam(courseId, quizId)
|
||||
.flatMap(exam -> examDAO.deleteOne(exam.id));
|
||||
return lmsSetupDAO
|
||||
.getLmsSetupIdByConnectionId(lmsUUID)
|
||||
.flatMap(lmsAPIService::getLmsAPITemplate)
|
||||
.map(findQuizData(courseId, quizId))
|
||||
.flatMap(this::findExam)
|
||||
.map(this::checkDeletion)
|
||||
.map(this::logExamDeleted)
|
||||
.flatMap(deleteExamAction::deleteExamFromLMSIntegration);
|
||||
}
|
||||
|
||||
private Exam checkDeletion(final Exam exam) {
|
||||
// TODO check if Exam can be deleted according to the Spec
|
||||
|
||||
// check if there are no active SEB client connections
|
||||
if (this.examSessionService.hasActiveSEBClientConnections(exam.id)) {
|
||||
throw new APIMessage.APIMessageException(
|
||||
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
|
||||
.of("Exam currently has active SEB Client connections."));
|
||||
}
|
||||
|
||||
return exam;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -244,7 +287,39 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
final String courseId,
|
||||
final String quizId,
|
||||
final OutputStream out) {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
|
||||
try {
|
||||
|
||||
final Result<Exam> examResult = lmsSetupDAO
|
||||
.getLmsSetupIdByConnectionId(lmsUUID)
|
||||
.flatMap(lmsAPIService::getLmsAPITemplate)
|
||||
.map(findQuizData(courseId, quizId))
|
||||
.flatMap(this::findExam);
|
||||
|
||||
if (examResult.hasError()) {
|
||||
throw new APIMessage.APIMessageException(APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("Exam not found"));
|
||||
}
|
||||
|
||||
final Exam exam = examResult.get();
|
||||
|
||||
String connectionConfigId = exam.getAdditionalAttribute(Exam.ADDITIONAL_ATTR_DEFAULT_CONNECTION_CONFIGURATION);
|
||||
if (StringUtils.isBlank(connectionConfigId)) {
|
||||
connectionConfigId = this.sebClientConfigDAO
|
||||
.all(exam.institutionId, true)
|
||||
.map(all -> all.iterator().next())
|
||||
.map(SEBClientConfig::getModelId)
|
||||
.getOr(null);
|
||||
}
|
||||
if (StringUtils.isBlank(connectionConfigId)) {
|
||||
return Result.ofRuntimeError("No active Connection Configuration found");
|
||||
}
|
||||
|
||||
this.clientConfigService.exportSEBClientConfiguration(out, connectionConfigId, exam.id);
|
||||
return Result.EMPTY;
|
||||
|
||||
} catch (final Exception e) {
|
||||
return Result.ofError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Function<LmsAPITemplate, QuizData> findQuizData(
|
||||
|
@ -260,42 +335,64 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
|
||||
return lmsAPITemplate
|
||||
.getQuiz(internalQuizId)
|
||||
.getOrThrow();
|
||||
.onError(error -> log.error("Failed to find quiz-data for id: {}", quizId))
|
||||
// this is only for debugging until Moodle Plugin is ready
|
||||
.getOr(new QuizData(
|
||||
MoodleUtils.getInternalQuizId(quizId, courseId, "MoodlePluginMockQuiz", null),
|
||||
lmsAPITemplate.lmsSetup().institutionId,
|
||||
lmsAPITemplate.lmsSetup().id,
|
||||
lmsAPITemplate.lmsSetup().lmsType,
|
||||
"MoodlePluginMockQuiz",
|
||||
"",
|
||||
DateTime.now(),
|
||||
DateTime.now().plusDays(1),
|
||||
"https://mockmoodle/swvgfrwef.sdvw",
|
||||
null
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
private Result<Exam> findExam(
|
||||
final String courseId,
|
||||
final String quizId) {
|
||||
|
||||
final String externalIdLike = quizId + Constants.COLON + courseId + Constants.PERCENTAGE;
|
||||
return examDAO.byExternalIdLike(externalIdLike);
|
||||
private Result<Exam> findExam(final QuizData quizData) {
|
||||
return examDAO.byExternalIdLike(quizData.id);
|
||||
}
|
||||
|
||||
private Function<QuizData, Exam> createExam(
|
||||
private Function<QuizData, Exam> createAccountAndExam(
|
||||
final String examTemplateId,
|
||||
final String quitPassword) {
|
||||
|
||||
return quizData -> {
|
||||
|
||||
final SEBServerUser currentUser = userService.getCurrentUser();
|
||||
|
||||
// check if the exam has already been imported, If so return the existing exam
|
||||
final Result<Exam> existingExam = findExam(quizData);
|
||||
if (!existingExam.hasError()) {
|
||||
// TODO do we need to check if ad-hoc account exists and if not, create one?
|
||||
return existingExam.get();
|
||||
}
|
||||
|
||||
// import exam
|
||||
final POSTMapper post = new POSTMapper(null, null);
|
||||
post.putIfAbsent(Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, examTemplateId);
|
||||
if (StringUtils.isNotBlank(quitPassword)) {
|
||||
post.putIfAbsent(Domain.EXAM.ATTR_QUIT_PASSWORD, quitPassword);
|
||||
}
|
||||
|
||||
final Exam exam = new Exam(null, quizData, post);
|
||||
final String accountUUID = createAdHocSupporterAccount(quizData);
|
||||
post.putIfAbsent(Domain.EXAM.ATTR_OWNER, accountUUID);
|
||||
|
||||
final Exam exam = new Exam(null, quizData, post);
|
||||
return examDAO
|
||||
.createNew(exam)
|
||||
.flatMap(examAdminService::applyExamImportInitialization)
|
||||
.map(this::logExamCreated)
|
||||
.getOrThrow();
|
||||
};
|
||||
}
|
||||
|
||||
private Exam createAdHocSupporterAccount(final Exam exam) {
|
||||
private String createAdHocSupporterAccount(final QuizData data) {
|
||||
// TODO create an ad hoc supporter account for this exam and apply it to the exam
|
||||
return exam;
|
||||
return "mockAccountUUID";
|
||||
}
|
||||
|
||||
private void deleteAdHocAccount(final Long examId) {
|
||||
|
@ -319,4 +416,22 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
private String getAPIRootURL() {
|
||||
return webserviceInfo.getExternalServerURL() + lmsAPIEndpoint;
|
||||
}
|
||||
|
||||
private Exam logExamCreated(final Exam exam) {
|
||||
this.userActivityLogDAO
|
||||
.logCreate(exam)
|
||||
.onError(error -> log.warn("Failed to log exam creation from LMS: {}", error.getMessage()));
|
||||
return exam;
|
||||
}
|
||||
|
||||
private Exam logExamDeleted(final Exam exam) {
|
||||
this.userActivityLogDAO
|
||||
.logDelete(exam)
|
||||
.onError(error -> log.warn("Failed to log exam deletion from LMS: {}", error.getMessage()));
|
||||
return exam;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -38,11 +40,6 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
|
||||
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.lms.APITemplateDataSupplier;
|
||||
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.LmsAPITemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.QuizLookupService;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
|
@ -56,6 +53,7 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
private final ClientCredentialService clientCredentialService;
|
||||
private final QuizLookupService quizLookupService;
|
||||
private final EnumMap<LmsType, LmsAPITemplateFactory> templateFactories;
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private final Map<CacheKey, LmsAPITemplate> cache = new ConcurrentHashMap<>();
|
||||
|
||||
|
@ -64,17 +62,19 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
final LmsSetupDAO lmsSetupDAO,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
final QuizLookupService quizLookupService,
|
||||
final ApplicationEventPublisher applicationEventPublisher,
|
||||
final Collection<LmsAPITemplateFactory> lmsAPITemplateFactories) {
|
||||
|
||||
this.webserviceInfo = webserviceInfo;
|
||||
this.lmsSetupDAO = lmsSetupDAO;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
this.quizLookupService = quizLookupService;
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
|
||||
final Map<LmsType, LmsAPITemplateFactory> factories = lmsAPITemplateFactories
|
||||
.stream()
|
||||
.collect(Collectors.toMap(
|
||||
t -> t.lmsType(),
|
||||
LmsAPITemplateFactory::lmsType,
|
||||
Function.identity()));
|
||||
this.templateFactories = new EnumMap<>(factories);
|
||||
}
|
||||
|
@ -158,14 +158,31 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
|||
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
|
||||
return lmsSetupTestResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (template.lmsSetup().getLmsType().features.contains(LmsSetup.Features.LMS_FULL_INTEGRATION)) {
|
||||
final Long lmsSetupId = template.lmsSetup().id;
|
||||
final LmsSetupTestResult lmsSetupTestResult = template.testFullIntegrationAPI();
|
||||
if (!lmsSetupTestResult.isOk()) {
|
||||
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
|
||||
this.lmsSetupDAO
|
||||
.setIntegrationActive(lmsSetupId, false)
|
||||
.onError(er -> log.error("Failed to mark LMS integration inactive", er));
|
||||
return lmsSetupTestResult;
|
||||
} else {
|
||||
// try to apply full integration with a change LMSSetup notification
|
||||
try {
|
||||
applicationEventPublisher.publishEvent(new LmsSetupChangeEvent(template.lmsSetup()));
|
||||
return lmsSetupTestResult;
|
||||
} catch (final Exception e) {
|
||||
log.warn(
|
||||
"Failed to apply full LMS integration on test attempt: lms: {} error: {}",
|
||||
template.lmsSetup(),
|
||||
e.getMessage());
|
||||
return LmsSetupTestResult.ofFullIntegrationAPIError(
|
||||
template.lmsSetup().lmsType,
|
||||
"Failed to apply full LMS integration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@ public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
log.info("Redirect to login after unauthorized request");
|
||||
response.getOutputStream().println("{ \"error\": \"" + exception.getMessage() + "\" }");
|
||||
},
|
||||
EXAM_API_RESOURCE_ID,
|
||||
LMS_API_RESOURCE_ID,
|
||||
apiEndpoint,
|
||||
true,
|
||||
4,
|
||||
|
|
|
@ -84,8 +84,8 @@ public class LmsIntegrationController {
|
|||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.LMS_FULL_INTEGRATION_EXAM_ENDPOINT,
|
||||
method = RequestMethod.DELETE,
|
||||
path = API.LMS_FULL_INTEGRATION_CONNECTION_CONFIG_ENDPOINT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public void getConnectionConfiguration(
|
||||
|
|
Loading…
Reference in a new issue