SEBSERV-301 moved mockup to test and improved LMSSetup test function

This commit is contained in:
anhefti 2023-01-12 08:04:40 +01:00
parent 3a86db9ba0
commit ed180603f8
9 changed files with 64 additions and 431 deletions

View file

@ -32,7 +32,8 @@ public final class LmsSetupTestResult {
MISSING_ATTRIBUTE, MISSING_ATTRIBUTE,
TOKEN_REQUEST, TOKEN_REQUEST,
QUIZ_ACCESS_API_REQUEST, QUIZ_ACCESS_API_REQUEST,
QUIZ_RESTRICTION_API_REQUEST QUIZ_RESTRICTION_API_REQUEST,
TEMPLATE_CREATION
} }
@JsonProperty(Domain.LMS_SETUP.ATTR_LMS_TYPE) @JsonProperty(Domain.LMS_SETUP.ATTR_LMS_TYPE)
@ -59,7 +60,7 @@ public final class LmsSetupTestResult {
Collections.emptyList()); Collections.emptyList());
} }
protected LmsSetupTestResult(final LmsSetup.LmsType lmsType, final Error error) { public LmsSetupTestResult(final LmsSetup.LmsType lmsType, final Error error) {
this(lmsType, this(lmsType,
Utils.immutableCollectionOf(Arrays.asList(error)), Utils.immutableCollectionOf(Arrays.asList(error)),
Collections.emptyList()); Collections.emptyList());
@ -163,7 +164,6 @@ public final class LmsSetupTestResult {
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }
} }
} }

View file

@ -479,6 +479,11 @@ public class LmsSetupForm implements TemplateComposer {
Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message)))); Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message))));
} }
case QUIZ_ACCESS_API_REQUEST: { case QUIZ_ACCESS_API_REQUEST: {
if (error.message.contains("quizaccess_sebserver_get_exams")) {
throw new PageMessageException(new LocTextKey(
"sebserver.lmssetup.action.test.quizRequestError.moodle.missing.plugin",
Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message))));
}
throw new PageMessageException(new LocTextKey( throw new PageMessageException(new LocTextKey(
"sebserver.lmssetup.action.test.quizRequestError", "sebserver.lmssetup.action.test.quizRequestError",
Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message)))); Utils.formatHTMLLinesForceEscaped(Utils.escapeHTML_XML_EcmaScript(error.message))));

View file

@ -24,7 +24,6 @@ import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
import ch.ethz.seb.sebserver.gbl.client.ProxyData; import ch.ethz.seb.sebserver.gbl.client.ProxyData;
@ -33,12 +32,12 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; 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.LmsSetupDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; 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.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
@ -133,17 +132,13 @@ public class LmsAPIServiceImpl implements LmsAPIService {
public Result<LmsAPITemplate> getLmsAPITemplate(final String lmsSetupId) { public Result<LmsAPITemplate> getLmsAPITemplate(final String lmsSetupId) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
synchronized (this) { synchronized (this) {
LmsAPITemplate lmsAPITemplate = getFromCache(lmsSetupId); final LmsAPITemplate lmsAPITemplate = getFromCache(lmsSetupId);
if (lmsAPITemplate == null) { if (lmsAPITemplate == null) {
lmsAPITemplate = createLmsSetupTemplate(lmsSetupId); return createLmsSetupTemplate(lmsSetupId)
if (lmsAPITemplate != null) { .onError(error -> log.error("Failed to create LMSSetup: ", error))
this.cache.put(new CacheKey(lmsSetupId, System.currentTimeMillis()), lmsAPITemplate); .onSuccess(t -> this.cache.put(new CacheKey(lmsSetupId, System.currentTimeMillis()), t))
} .getOrThrow();
} }
if (lmsAPITemplate == null) {
throw new ResourceNotFoundException(EntityType.LMS_SETUP, lmsSetupId);
}
return lmsAPITemplate; return lmsAPITemplate;
} }
}); });
@ -153,10 +148,12 @@ public class LmsAPIServiceImpl implements LmsAPIService {
public LmsSetupTestResult test(final LmsAPITemplate template) { public LmsSetupTestResult test(final LmsAPITemplate template) {
final LmsSetupTestResult testCourseAccessAPI = template.testCourseAccessAPI(); final LmsSetupTestResult testCourseAccessAPI = template.testCourseAccessAPI();
if (!testCourseAccessAPI.isOk()) { if (!testCourseAccessAPI.isOk()) {
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
return testCourseAccessAPI; return testCourseAccessAPI;
} }
if (template.lmsSetup().getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION)) { if (template.lmsSetup().getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
this.cache.remove(new CacheKey(template.lmsSetup().getModelId(), 0));
return template.testCourseRestrictionAPI(); return template.testCourseRestrictionAPI();
} }
@ -169,7 +166,15 @@ public class LmsAPIServiceImpl implements LmsAPIService {
lmsSetup, lmsSetup,
this.clientCredentialService); this.clientCredentialService);
final LmsAPITemplate lmsSetupTemplate = createLmsSetupTemplate(apiTemplateDataSupplier); final Result<LmsAPITemplate> createLmsSetupTemplate = createLmsSetupTemplate(apiTemplateDataSupplier);
if (createLmsSetupTemplate.hasError()) {
return new LmsSetupTestResult(
lmsSetup.lmsType,
new LmsSetupTestResult.Error(ErrorType.TEMPLATE_CREATION,
createLmsSetupTemplate.getError().getMessage()));
}
final LmsAPITemplate lmsSetupTemplate = createLmsSetupTemplate.get();
final LmsSetupTestResult testCourseAccessAPI = lmsSetupTemplate.testCourseAccessAPI(); final LmsSetupTestResult testCourseAccessAPI = lmsSetupTemplate.testCourseAccessAPI();
if (!testCourseAccessAPI.isOk()) { if (!testCourseAccessAPI.isOk()) {
@ -183,40 +188,6 @@ public class LmsAPIServiceImpl implements LmsAPIService {
return LmsSetupTestResult.ofOkay(lmsSetupTemplate.lmsSetup().getLmsType()); return LmsSetupTestResult.ofOkay(lmsSetupTemplate.lmsSetup().getLmsType());
} }
// /** Collect all QuizData from all affecting LmsSetup.
// * If filterMap contains a LmsSetup identifier, only the QuizData from that LmsSetup is collected.
// * Otherwise QuizData from all active LmsSetup of the current institution are collected.
// *
// * @param filterMap the FilterMap containing either an LmsSetup identifier or an institution identifier
// * @return list of QuizData from all affecting LmsSetup */
// private Result<List<QuizData>> getAllQuizzesFromLMSSetups(final FilterMap filterMap) {
//
// // case 1. if lmsSetupId is available only get quizzes from specified LmsSetup
// final Long lmsSetupId = filterMap.getLmsSetupId();
// if (lmsSetupId != null) {
// return getQuizzesForSingleLMS(filterMap, lmsSetupId);
// }
//
// // case 2. get quizzes from all LmsSetups of specified institution
// return this.quizLookupService.getAllQuizzesFromLMSSetups(
// filterMap,
// this::getLmsAPITemplate);
//
//// final Long institutionId = filterMap.getInstitutionId();
//// return this.lmsSetupDAO.all(institutionId, true)
//// .getOrThrow()
//// .parallelStream()
//// .map(LmsSetup::getModelId)
//// .map(this::getLmsAPITemplate)
//// .flatMap(Result::onErrorLogAndSkip)
//// .map(template -> template.getQuizzes(filterMap))
//// .flatMap(Result::onErrorLogAndSkip)
//// .flatMap(List::stream)
//// .distinct()
//// .collect(Collectors.toList());
//
// }
private LmsAPITemplate getFromCache(final String lmsSetupId) { private LmsAPITemplate getFromCache(final String lmsSetupId) {
// first cleanup the cache by removing old instances // first cleanup the cache by removing old instances
final long currentTimeMillis = System.currentTimeMillis(); final long currentTimeMillis = System.currentTimeMillis();
@ -245,7 +216,7 @@ public class LmsAPIServiceImpl implements LmsAPIService {
return lmsAPITemplate; return lmsAPITemplate;
} }
private LmsAPITemplate createLmsSetupTemplate(final String lmsSetupId) { private Result<LmsAPITemplate> createLmsSetupTemplate(final String lmsSetupId) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Create new LmsAPITemplate for id: {}", lmsSetupId); log.debug("Create new LmsAPITemplate for id: {}", lmsSetupId);
@ -256,7 +227,7 @@ public class LmsAPIServiceImpl implements LmsAPIService {
this.lmsSetupDAO)); this.lmsSetupDAO));
} }
private LmsAPITemplate createLmsSetupTemplate(final APITemplateDataSupplier apiTemplateDataSupplier) { private Result<LmsAPITemplate> createLmsSetupTemplate(final APITemplateDataSupplier apiTemplateDataSupplier) {
final LmsType lmsType = apiTemplateDataSupplier.getLmsSetup().lmsType; final LmsType lmsType = apiTemplateDataSupplier.getLmsSetup().lmsType;
@ -268,8 +239,7 @@ public class LmsAPIServiceImpl implements LmsAPIService {
.get(lmsType); .get(lmsType);
return lmsAPITemplateFactory return lmsAPITemplateFactory
.create(apiTemplateDataSupplier) .create(apiTemplateDataSupplier);
.getOrThrow();
} }
/** Used to always get the actual LMS connection data from persistent */ /** Used to always get the actual LMS connection data from persistent */

View file

@ -1,313 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleUtils.MoodleQuizRestriction;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseAccess;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseRestriction;
@Deprecated
public class MockupRestTemplateFactory implements MoodleRestTemplateFactory {
private final APITemplateDataSupplier apiTemplateDataSupplier;
public MockupRestTemplateFactory(final APITemplateDataSupplier apiTemplateDataSupplier) {
this.apiTemplateDataSupplier = apiTemplateDataSupplier;
}
@Override
public LmsSetupTestResult test() {
return LmsSetupTestResult.ofOkay(LmsType.MOODLE_PLUGIN);
}
@Override
public APITemplateDataSupplier getApiTemplateDataSupplier() {
return this.apiTemplateDataSupplier;
}
@Override
public Set<String> getKnownTokenAccessPaths() {
final Set<String> paths = new HashSet<>();
paths.add(MoodleAPIRestTemplate.MOODLE_DEFAULT_TOKEN_REQUEST_PATH);
return paths;
}
@Override
public Result<MoodleAPIRestTemplate> createRestTemplate() {
return Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
}
@Override
public Result<MoodleAPIRestTemplate> createRestTemplate(final String accessTokenPath) {
return Result.of(new MockupMoodleRestTemplate(this.apiTemplateDataSupplier.getLmsSetup().lmsApiUrl));
}
public static final class MockupMoodleRestTemplate implements MoodleAPIRestTemplate {
private final String accessToken = UUID.randomUUID().toString();
private final String url;
public MockupMoodleRestTemplate(final String url) {
this.url = url;
}
@Override
public String getService() {
return "mockup-service";
}
@Override
public void setService(final String service) {
}
@Override
public CharSequence getAccessToken() {
System.out.println("***** getAccessToken: " + this.accessToken);
return this.accessToken;
}
@Override
public void testAPIConnection(final String... functions) {
System.out.println("***** testAPIConnection functions: " + functions);
}
@Override
public String callMoodleAPIFunction(final String functionName) {
return callMoodleAPIFunction(functionName, null, null);
}
@Override
public String callMoodleAPIFunction(
final String functionName,
final MultiValueMap<String, String> queryAttributes) {
return callMoodleAPIFunction(functionName, null, queryAttributes);
}
@Override
public String callMoodleAPIFunction(
final String functionName,
final MultiValueMap<String, String> queryParams,
final MultiValueMap<String, String> queryAttributes) {
final UriComponentsBuilder queryParam = UriComponentsBuilder
.fromHttpUrl(this.url + MOODLE_DEFAULT_REST_API_PATH)
.queryParam(REST_REQUEST_TOKEN_NAME, this.accessToken)
.queryParam(REST_REQUEST_FUNCTION_NAME, functionName)
.queryParam(REST_REQUEST_FORMAT_NAME, "json");
if (queryParams != null && !queryParams.isEmpty()) {
queryParam.queryParams(queryParams);
}
final boolean usePOST = queryAttributes != null && !queryAttributes.isEmpty();
HttpEntity<?> functionReqEntity;
if (usePOST) {
final HttpHeaders headers = new HttpHeaders();
headers.set(
HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_FORM_URLENCODED_VALUE);
final String body = Utils.toAppFormUrlEncodedBody(queryAttributes);
functionReqEntity = new HttpEntity<>(body, headers);
} else {
functionReqEntity = new HttpEntity<>(new LinkedMultiValueMap<>());
}
System.out.println("***** callMoodleAPIFunction HttpEntity: " + functionReqEntity);
if (MoodlePluginCourseAccess.COURSES_API_FUNCTION_NAME.equals(functionName)) {
return respondCourses(queryAttributes);
} else if (MoodlePluginCourseAccess.USERS_API_FUNCTION_NAME.equals(functionName)) {
return respondUsers(queryAttributes);
} else if (MoodlePluginCourseRestriction.RESTRICTION_GET_FUNCTION_NAME.equals(functionName)) {
return respondGetRestriction(
queryParams.getFirst(MoodlePluginCourseRestriction.ATTRIBUTE_QUIZ_ID),
queryAttributes);
} else if (MoodlePluginCourseRestriction.RESTRICTION_SET_FUNCTION_NAME.equals(functionName)) {
return respondSetRestriction(
queryParams.getFirst(MoodlePluginCourseRestriction.ATTRIBUTE_QUIZ_ID),
queryAttributes);
}
else {
throw new RuntimeException("Unknown function: " + functionName);
}
}
@SuppressWarnings("unused")
private static final class MockCD {
public final String id;
public final String shortname;
public final String categoryid;
public final String fullname;
public final String displayname;
public final String idnumber;
public final Long startdate; // unix-time seconds UTC
public final Long enddate; // unix-time seconds UTC
public final Long timecreated; // unix-time seconds UTC
public final boolean visible;
public final Collection<MockQ> quizzes;
public MockCD(final String num, final Collection<MockQ> quizzes) {
this.id = num;
this.shortname = "c" + num;
this.categoryid = "mock";
this.fullname = "course" + num;
this.displayname = this.fullname;
this.idnumber = "i" + num;
this.startdate = Long.valueOf(num);
this.enddate = null;
this.timecreated = Long.valueOf(num);
this.visible = true;
this.quizzes = quizzes;
}
}
@SuppressWarnings("unused")
private static final class MockQ {
public final String id;
public final String coursemodule;
public final String course;
public final String name;
public final String intro;
public final Long timeopen; // unix-time seconds UTC
public final Long timeclose; // unix-time seconds UTC
public MockQ(final String courseId, final String num) {
this.id = num;
this.coursemodule = courseId;
this.course = courseId;
this.name = "quiz " + num;
this.intro = this.name;
this.timeopen = Long.valueOf(num);
this.timeclose = null;
}
}
private String respondCourses(final MultiValueMap<String, String> queryAttributes) {
try {
final List<String> ids = queryAttributes.get(MoodlePluginCourseAccess.PARAM_COURSE_ID);
final String from = queryAttributes.getFirst(MoodlePluginCourseAccess.PARAM_PAGE_START);
System.out.println("************* from: " + from);
final List<MockCD> courses;
if (ids != null && !ids.isEmpty()) {
courses = ids
.stream()
.map(id -> new MockCD(
id,
getQuizzesForCourse(Integer.parseInt(id))))
.collect(Collectors.toList());
} else if (from != null && Integer.valueOf(from) < 11) {
courses = new ArrayList<>();
final int num = (Integer.valueOf(from) > 0) ? 10 : 1;
for (int i = 0; i < 10; i++) {
courses.add(new MockCD(String.valueOf(num + i), getQuizzesForCourse(num + i)));
}
} else {
courses = new ArrayList<>();
}
final Map<String, Object> response = new HashMap<>();
response.put("results", courses);
final JSONMapper jsonMapper = new JSONMapper();
final String result = jsonMapper.writeValueAsString(response);
System.out.println("******** courses response: " + result);
return result;
} catch (final JsonProcessingException e) {
e.printStackTrace();
return "";
}
}
private final Map<String, MoodleQuizRestriction> restrcitions = new HashMap<>();
private String respondSetRestriction(final String quizId, final MultiValueMap<String, String> queryAttributes) {
final List<String> configKeys = queryAttributes.get(MoodlePluginCourseRestriction.ATTRIBUTE_CONFIG_KEYS);
final List<String> beks = queryAttributes.get(MoodlePluginCourseRestriction.ATTRIBUTE_BROWSER_EXAM_KEYS);
final String quitURL = queryAttributes.getFirst(MoodlePluginCourseRestriction.ATTRIBUTE_QUIT_URL);
final String quitSecret = queryAttributes.getFirst(MoodlePluginCourseRestriction.ATTRIBUTE_QUIT_SECRET);
final MoodleQuizRestriction moodleQuizRestriction = new MoodleQuizRestriction(
quizId,
StringUtils.join(configKeys, Constants.LIST_SEPARATOR),
StringUtils.join(beks, Constants.LIST_SEPARATOR),
quitURL,
quitSecret);
this.restrcitions.put(quizId, moodleQuizRestriction);
final JSONMapper jsonMapper = new JSONMapper();
try {
return jsonMapper.writeValueAsString(moodleQuizRestriction);
} catch (final JsonProcessingException e) {
e.printStackTrace();
return "";
}
}
private String respondGetRestriction(final String quizId, final MultiValueMap<String, String> queryAttributes) {
final MoodleQuizRestriction moodleQuizRestriction = this.restrcitions.get(quizId);
if (moodleQuizRestriction != null) {
final JSONMapper jsonMapper = new JSONMapper();
try {
return jsonMapper.writeValueAsString(moodleQuizRestriction);
} catch (final JsonProcessingException e) {
e.printStackTrace();
return "";
}
}
return "";
}
private Collection<MockQ> getQuizzesForCourse(final int courseId) {
final String id = String.valueOf(courseId);
final Collection<MockQ> result = new ArrayList<>();
result.add(new MockQ(id, "10" + id));
if (courseId % 2 > 0) {
result.add(new MockQ(id, "11" + id));
}
return result;
}
private String respondUsers(final MultiValueMap<String, String> queryAttributes) {
// TODO
return "";
}
}
}

View file

@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
@ -25,52 +24,39 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamConfigurationValueService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; 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.LmsAPITemplateFactory;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodlePluginCheck;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactoryImpl; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactoryImpl;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseAccess;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseRestriction;
@Lazy @Lazy
@Service @Service
@WebServiceProfile @WebServiceProfile
public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory { public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
private final MoodlePluginCheck moodlePluginCheck;
private final JSONMapper jsonMapper; private final JSONMapper jsonMapper;
private final CacheManager cacheManager;
private final AsyncService asyncService; private final AsyncService asyncService;
private final Environment environment; private final Environment environment;
private final ClientCredentialService clientCredentialService; private final ClientCredentialService clientCredentialService;
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
private final ExamConfigurationValueService examConfigurationValueService;
private final ApplicationContext applicationContext; private final ApplicationContext applicationContext;
private final String[] alternativeTokenRequestPaths; private final String[] alternativeTokenRequestPaths;
protected MoodleLmsAPITemplateFactory( protected MoodleLmsAPITemplateFactory(
final MoodlePluginCheck moodlePluginCheck,
final JSONMapper jsonMapper, final JSONMapper jsonMapper,
final CacheManager cacheManager,
final AsyncService asyncService, final AsyncService asyncService,
final Environment environment, final Environment environment,
final ClientCredentialService clientCredentialService, final ClientCredentialService clientCredentialService,
final ExamConfigurationValueService examConfigurationValueService,
final ClientHttpRequestFactoryService clientHttpRequestFactoryService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
final ApplicationContext applicationContext, final ApplicationContext applicationContext,
@Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) { @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) {
this.moodlePluginCheck = moodlePluginCheck;
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
this.cacheManager = cacheManager;
this.asyncService = asyncService; this.asyncService = asyncService;
this.environment = environment; this.environment = environment;
this.clientCredentialService = clientCredentialService; this.clientCredentialService = clientCredentialService;
this.examConfigurationValueService = examConfigurationValueService;
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null) this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null)
@ -100,43 +86,20 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
this.clientHttpRequestFactoryService, this.clientHttpRequestFactoryService,
this.alternativeTokenRequestPaths); this.alternativeTokenRequestPaths);
if (this.moodlePluginCheck.checkPluginAvailable(restTemplateFactory)) { final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(
this.jsonMapper,
this.asyncService,
restTemplateFactory,
asyncLoaderPrototype,
this.environment);
final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess( return new LmsAPITemplateAdapter(
this.jsonMapper, this.asyncService,
this.asyncService, this.environment,
restTemplateFactory, apiTemplateDataSupplier,
this.cacheManager, moodleCourseAccess,
this.environment); new MoodleCourseRestriction());
final MoodlePluginCourseRestriction moodlePluginCourseRestriction = new MoodlePluginCourseRestriction(
this.jsonMapper,
restTemplateFactory,
this.examConfigurationValueService);
return new LmsAPITemplateAdapter(
this.asyncService,
this.environment,
apiTemplateDataSupplier,
moodlePluginCourseAccess,
moodlePluginCourseRestriction);
} else {
final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(
this.jsonMapper,
this.asyncService,
restTemplateFactory,
asyncLoaderPrototype,
this.environment);
return new LmsAPITemplateAdapter(
this.asyncService,
this.environment,
apiTemplateDataSupplier,
moodleCourseAccess,
new MoodleCourseRestriction());
}
}); });
} }

View file

@ -160,7 +160,6 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
USERS_API_FUNCTION_NAME); USERS_API_FUNCTION_NAME);
} catch (final RuntimeException e) { } catch (final RuntimeException e) {
log.error("Failed to access Moodle course API: ", e);
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.MOODLE_PLUGIN, e.getMessage()); return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.MOODLE_PLUGIN, e.getMessage());
} }

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -29,9 +28,9 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; 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.LmsAPITemplateFactory;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MockupRestTemplateFactory;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodlePluginCheck; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodlePluginCheck;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactoryImpl;
@Lazy @Lazy
@Service @Service
@ -46,7 +45,6 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
private final ClientCredentialService clientCredentialService; private final ClientCredentialService clientCredentialService;
private final ExamConfigurationValueService examConfigurationValueService; private final ExamConfigurationValueService examConfigurationValueService;
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService; private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
private final ApplicationContext applicationContext;
private final String[] alternativeTokenRequestPaths; private final String[] alternativeTokenRequestPaths;
protected MooldePluginLmsAPITemplateFactory( protected MooldePluginLmsAPITemplateFactory(
@ -58,7 +56,6 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
final ClientCredentialService clientCredentialService, final ClientCredentialService clientCredentialService,
final ExamConfigurationValueService examConfigurationValueService, final ExamConfigurationValueService examConfigurationValueService,
final ClientHttpRequestFactoryService clientHttpRequestFactoryService, final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
final ApplicationContext applicationContext,
@Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) { @Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) {
this.moodlePluginCheck = moodlePluginCheck; this.moodlePluginCheck = moodlePluginCheck;
@ -69,7 +66,6 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
this.clientCredentialService = clientCredentialService; this.clientCredentialService = clientCredentialService;
this.examConfigurationValueService = examConfigurationValueService; this.examConfigurationValueService = examConfigurationValueService;
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService; this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
this.applicationContext = applicationContext;
this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null) this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null)
? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR) ? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR)
: null; : null;
@ -84,15 +80,16 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
public Result<LmsAPITemplate> create(final APITemplateDataSupplier apiTemplateDataSupplier) { public Result<LmsAPITemplate> create(final APITemplateDataSupplier apiTemplateDataSupplier) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
// final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactoryImpl( final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactoryImpl(
// this.jsonMapper, this.jsonMapper,
// apiTemplateDataSupplier, apiTemplateDataSupplier,
// this.clientCredentialService, this.clientCredentialService,
// this.clientHttpRequestFactoryService, this.clientHttpRequestFactoryService,
// this.alternativeTokenRequestPaths); this.alternativeTokenRequestPaths);
final MoodleRestTemplateFactory moodleRestTemplateFactory = // if (!this.moodlePluginCheck.checkPluginAvailable(moodleRestTemplateFactory)) {
new MockupRestTemplateFactory(apiTemplateDataSupplier); // throw new RuntimeException("Unable to detect SEB Server Moodle integration plugin!");
// }
final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess( final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess(
this.jsonMapper, this.jsonMapper,

View file

@ -28,7 +28,9 @@ import ch.ethz.seb.sebserver.gbl.api.authorization.PrivilegeType;
import ch.ethz.seb.sebserver.gbl.model.Domain; import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity; import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult.ErrorType;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport;
@ -93,9 +95,16 @@ public class LmsSetupController extends ActivatableEntityController<LmsSetup, Lm
EntityType.LMS_SETUP, EntityType.LMS_SETUP,
institutionId); institutionId);
final LmsSetupTestResult result = this.lmsAPIService.getLmsAPITemplate(modelId) final LmsSetupTestResult result = this.lmsAPIService
.getLmsAPITemplate(modelId)
.map(this.lmsAPIService::test) .map(this.lmsAPIService::test)
.getOrThrow(); .onErrorDo(error -> {
final LmsType lmsType = this.entityDAO.byPK(modelId).get().lmsType;
return new LmsSetupTestResult(
lmsType,
new LmsSetupTestResult.Error(ErrorType.TEMPLATE_CREATION, error.getMessage()));
})
.get();
if (result.missingLMSSetupAttribute != null && !result.missingLMSSetupAttribute.isEmpty()) { if (result.missingLMSSetupAttribute != null && !result.missingLMSSetupAttribute.isEmpty()) {
throw new APIMessageException(result.missingLMSSetupAttribute); throw new APIMessageException(result.missingLMSSetupAttribute);

View file

@ -328,6 +328,8 @@ sebserver.useraccount.delete.confirm.message.noDeps=The User Account ({0}) was s
sebserver.lmssetup.type.MOCKUP=Testing sebserver.lmssetup.type.MOCKUP=Testing
sebserver.lmssetup.type.MOODLE=Moodle sebserver.lmssetup.type.MOODLE=Moodle
sebserver.lmssetup.type.MOODLE_PLUGIN=Moodle Plugin
sebserver.lmssetup.type.MOODLE_PLUGIN.tooltip=Moodle with SEB Server integration plugin installed
sebserver.lmssetup.type.OPEN_EDX=Open edX sebserver.lmssetup.type.OPEN_EDX=Open edX
sebserver.lmssetup.type.ANS_DELFT=Ans Delft sebserver.lmssetup.type.ANS_DELFT=Ans Delft
sebserver.lmssetup.type.OPEN_OLAT=Open OLAT sebserver.lmssetup.type.OPEN_OLAT=Open OLAT
@ -365,6 +367,7 @@ sebserver.lmssetup.action.test.quizRestrictionError=Unable to access course rest
sebserver.lmssetup.action.test.features.error=The API access was granted but there is some missing functionality:<br/><br/>- Course Access: {0}<br/>- SEB Restriction: {1} sebserver.lmssetup.action.test.features.error=The API access was granted but there is some missing functionality:<br/><br/>- Course Access: {0}<br/>- SEB Restriction: {1}
sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup sebserver.lmssetup.action.test.missingParameter=There is one or more missing connection parameter.<br/>Please check the connection parameter for this LMS Setup
sebserver.lmssetup.action.test.unknownError=An unexpected error happened while trying to connect to the LMS course API. {0} sebserver.lmssetup.action.test.unknownError=An unexpected error happened while trying to connect to the LMS course API. {0}
sebserver.lmssetup.action.test.quizRequestError.moodle.missing.plugin=Moodle SEB Server integration plugin cannot be detected on this Moodle server.<br/>Please consider using the origin "Moodle" LMS Setup type or install the SEB Server integration plugin on this Moodle server.<br/><br/>For further information please refer to the <a target="_blank" href="https://seb-server.readthedocs.io/en/latest/#">documentation</a>
sebserver.lmssetup.action.save=Save LMS Setup sebserver.lmssetup.action.save=Save LMS Setup
sebserver.lmssetup.action.activate=Activate LMS Setup sebserver.lmssetup.action.activate=Activate LMS Setup
sebserver.lmssetup.action.deactivate=Deactivate LMS Setup sebserver.lmssetup.action.deactivate=Deactivate LMS Setup