SEBSERV-417 create delete exam from Moodle

This commit is contained in:
anhefti 2024-05-02 11:39:41 +02:00
parent c0919ce0cf
commit ff89864b19
14 changed files with 247 additions and 82 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -150,6 +150,4 @@ public interface ExamAdminService {
Result<Exam> applyQuitPassword(Exam exam);
Result<Exam> findExamByLmsIdentity(String courseId, String quizId, String identity);
}

View file

@ -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(() -> {

View file

@ -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 */

View file

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

View file

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

View file

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

View file

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