SEBSERV-301 implementation
This commit is contained in:
parent
a141eccfa1
commit
71ae9fc755
12 changed files with 471 additions and 108 deletions
|
@ -270,4 +270,14 @@ public class POSTMapper {
|
|||
this.params.putIfAbsent(name, Arrays.asList(value));
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("POSTMapper [params=");
|
||||
builder.append(this.params);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||
/** The Moodle binding features only the course access API so far */
|
||||
MOODLE(Features.COURSE_API /* , Features.SEB_RESTRICTION */),
|
||||
/** The Moodle binding features with SEB Server integration plugin for fully featured */
|
||||
MOODLE_PLUGIN(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||
/** The Ans Delft binding is on the way */
|
||||
ANS_DELFT(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||
/** The OpenOLAT binding is on the way */
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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 org.springframework.util.MultiValueMap;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public interface MoodleAPIRestTemplate {
|
||||
|
||||
String URI_VAR_USER_NAME = "username";
|
||||
String URI_VAR_PASSWORD = "pwd";
|
||||
String URI_VAR_SERVICE = "service";
|
||||
|
||||
String MOODLE_DEFAULT_TOKEN_REQUEST_PATH =
|
||||
"/login/token.php?username={" + URI_VAR_USER_NAME +
|
||||
"}&password={" + URI_VAR_PASSWORD + "}&service={" + URI_VAR_SERVICE + "}";
|
||||
|
||||
String MOODLE_DEFAULT_REST_API_PATH = "/webservice/rest/server.php";
|
||||
String REST_REQUEST_TOKEN_NAME = "wstoken";
|
||||
String REST_REQUEST_FUNCTION_NAME = "wsfunction";
|
||||
String REST_REQUEST_FORMAT_NAME = "moodlewsrestformat";
|
||||
|
||||
String getService();
|
||||
|
||||
void setService(String service);
|
||||
|
||||
CharSequence getAccessToken();
|
||||
|
||||
void testAPIConnection(String... functions);
|
||||
|
||||
String callMoodleAPIFunction(String functionName);
|
||||
|
||||
String callMoodleAPIFunction(
|
||||
final String functionName,
|
||||
final MultiValueMap<String, String> queryAttributes);
|
||||
|
||||
String callMoodleAPIFunction(
|
||||
final String functionName,
|
||||
final MultiValueMap<String, String> queryParams,
|
||||
final MultiValueMap<String, String> queryAttributes);
|
||||
|
||||
/** This maps a Moodle warning JSON object */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static final class Warning {
|
||||
final String item;
|
||||
final String itemid;
|
||||
final String warningcode;
|
||||
final String message;
|
||||
|
||||
@JsonCreator
|
||||
public Warning(
|
||||
@JsonProperty(value = "item") final String item,
|
||||
@JsonProperty(value = "itemid") final String itemid,
|
||||
@JsonProperty(value = "warningcode") final String warningcode,
|
||||
@JsonProperty(value = "message") final String message) {
|
||||
|
||||
this.item = item;
|
||||
this.itemid = itemid;
|
||||
this.warningcode = warningcode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Warning [item=");
|
||||
builder.append(this.item);
|
||||
builder.append(", itemid=");
|
||||
builder.append(this.itemid);
|
||||
builder.append(", warningcode=");
|
||||
builder.append(this.warningcode);
|
||||
builder.append(", message=");
|
||||
builder.append(this.message);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy;
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -57,11 +57,11 @@ public class MoodleRestTemplateFactory {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(MoodleRestTemplateFactory.class);
|
||||
|
||||
final JSONMapper jsonMapper;
|
||||
final APITemplateDataSupplier apiTemplateDataSupplier;
|
||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
|
||||
final ClientCredentialService clientCredentialService;
|
||||
final Set<String> knownTokenAccessPaths;
|
||||
public final JSONMapper jsonMapper;
|
||||
public final APITemplateDataSupplier apiTemplateDataSupplier;
|
||||
public final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
|
||||
public final ClientCredentialService clientCredentialService;
|
||||
public final Set<String> knownTokenAccessPaths;
|
||||
|
||||
public MoodleRestTemplateFactory(
|
||||
final JSONMapper jsonMapper,
|
||||
|
@ -75,11 +75,12 @@ public class MoodleRestTemplateFactory {
|
|||
this.clientCredentialService = clientCredentialService;
|
||||
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
|
||||
|
||||
this.knownTokenAccessPaths = new HashSet<>();
|
||||
this.knownTokenAccessPaths.add(MoodleAPIRestTemplate.MOODLE_DEFAULT_TOKEN_REQUEST_PATH);
|
||||
final Set<String> paths = new HashSet<>();
|
||||
paths.add(MoodleAPIRestTemplate.MOODLE_DEFAULT_TOKEN_REQUEST_PATH);
|
||||
if (alternativeTokenRequestPaths != null) {
|
||||
this.knownTokenAccessPaths.addAll(Arrays.asList(alternativeTokenRequestPaths));
|
||||
paths.addAll(Arrays.asList(alternativeTokenRequestPaths));
|
||||
}
|
||||
this.knownTokenAccessPaths = Utils.immutableSetOf(paths);
|
||||
}
|
||||
|
||||
APITemplateDataSupplier getApiTemplateDataSupplier() {
|
||||
|
@ -125,7 +126,7 @@ public class MoodleRestTemplateFactory {
|
|||
return LmsSetupTestResult.ofOkay(LmsType.MOODLE);
|
||||
}
|
||||
|
||||
Result<MoodleAPIRestTemplate> createRestTemplate() {
|
||||
public Result<MoodleAPIRestTemplate> createRestTemplate() {
|
||||
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
|
||||
|
@ -149,7 +150,7 @@ public class MoodleRestTemplateFactory {
|
|||
") on paths: " + this.knownTokenAccessPaths));
|
||||
}
|
||||
|
||||
Result<MoodleAPIRestTemplate> createRestTemplate(final String accessTokenPath) {
|
||||
public Result<MoodleAPIRestTemplate> createRestTemplate(final String accessTokenPath) {
|
||||
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
|
||||
|
@ -162,8 +163,9 @@ public class MoodleRestTemplateFactory {
|
|||
.getPlainClientSecret(credentials)
|
||||
.getOrThrow();
|
||||
|
||||
final MoodleAPIRestTemplate restTemplate = new MoodleAPIRestTemplate(
|
||||
final MoodleAPIRestTemplateImpl restTemplate = new MoodleAPIRestTemplateImpl(
|
||||
this.jsonMapper,
|
||||
this.apiTemplateDataSupplier,
|
||||
lmsSetup.lmsApiUrl,
|
||||
accessTokenPath,
|
||||
lmsSetup.lmsRestApiToken,
|
||||
|
@ -187,22 +189,13 @@ public class MoodleRestTemplateFactory {
|
|||
});
|
||||
}
|
||||
|
||||
public class MoodleAPIRestTemplate extends RestTemplate {
|
||||
public static class MoodleAPIRestTemplateImpl extends RestTemplate implements MoodleAPIRestTemplate {
|
||||
|
||||
public static final String URI_VAR_USER_NAME = "username";
|
||||
public static final String URI_VAR_PASSWORD = "pwd";
|
||||
public static final String URI_VAR_SERVICE = "service";
|
||||
|
||||
private static final String MOODLE_DEFAULT_TOKEN_REQUEST_PATH =
|
||||
"/login/token.php?username={" + URI_VAR_USER_NAME +
|
||||
"}&password={" + URI_VAR_PASSWORD + "}&service={" + URI_VAR_SERVICE + "}";
|
||||
|
||||
private static final String MOODLE_DEFAULT_REST_API_PATH = "/webservice/rest/server.php";
|
||||
private static final String REST_REQUEST_TOKEN_NAME = "wstoken";
|
||||
private static final String REST_REQUEST_FUNCTION_NAME = "wsfunction";
|
||||
private static final String REST_REQUEST_FORMAT_NAME = "moodlewsrestformat";
|
||||
private static final String REST_API_TEST_FUNCTION = "core_webservice_get_site_info";
|
||||
|
||||
final JSONMapper jsonMapper;
|
||||
final APITemplateDataSupplier apiTemplateDataSupplier;
|
||||
|
||||
private final String serverURL;
|
||||
private final String tokenPath;
|
||||
|
||||
|
@ -211,14 +204,18 @@ public class MoodleRestTemplateFactory {
|
|||
private final Map<String, String> tokenReqURIVars;
|
||||
private final HttpEntity<?> tokenReqEntity = new HttpEntity<>(new LinkedMultiValueMap<>());
|
||||
|
||||
protected MoodleAPIRestTemplate(
|
||||
protected MoodleAPIRestTemplateImpl(
|
||||
final JSONMapper jsonMapper,
|
||||
final APITemplateDataSupplier apiTemplateDataSupplier,
|
||||
final String serverURL,
|
||||
final String tokenPath,
|
||||
final CharSequence accessToken,
|
||||
final CharSequence username,
|
||||
final CharSequence password) {
|
||||
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.apiTemplateDataSupplier = apiTemplateDataSupplier;
|
||||
|
||||
this.serverURL = serverURL;
|
||||
this.tokenPath = tokenPath;
|
||||
this.accessToken = StringUtils.isNotBlank(accessToken) ? accessToken : null;
|
||||
|
@ -230,14 +227,17 @@ public class MoodleRestTemplateFactory {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getService() {
|
||||
return this.tokenReqURIVars.get(URI_VAR_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setService(final String service) {
|
||||
this.tokenReqURIVars.put(URI_VAR_SERVICE, service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getAccessToken() {
|
||||
if (this.accessToken == null) {
|
||||
requestAccessToken();
|
||||
|
@ -246,22 +246,27 @@ public class MoodleRestTemplateFactory {
|
|||
return this.accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAPIConnection(final String... functions) {
|
||||
try {
|
||||
final String apiInfo = this.callMoodleAPIFunction(REST_API_TEST_FUNCTION);
|
||||
final WebserviceInfo webserviceInfo =
|
||||
MoodleRestTemplateFactory.this.jsonMapper.readValue(apiInfo, WebserviceInfo.class);
|
||||
final WebserviceInfo webserviceInfo = this.jsonMapper.readValue(
|
||||
apiInfo,
|
||||
WebserviceInfo.class);
|
||||
|
||||
if (StringUtils.isBlank(webserviceInfo.username) || StringUtils.isBlank(webserviceInfo.userid)) {
|
||||
throw new RuntimeException("Invalid WebserviceInfo: " + webserviceInfo);
|
||||
}
|
||||
|
||||
final List<String> missingAPIFunctions = Arrays.stream(functions)
|
||||
.filter(f -> !webserviceInfo.functions.containsKey(f))
|
||||
.collect(Collectors.toList());
|
||||
if (functions != null) {
|
||||
|
||||
if (!missingAPIFunctions.isEmpty()) {
|
||||
throw new RuntimeException("Missing Moodle Webservice API functions: " + missingAPIFunctions);
|
||||
final List<String> missingAPIFunctions = Arrays.stream(functions)
|
||||
.filter(f -> !webserviceInfo.functions.containsKey(f))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!missingAPIFunctions.isEmpty()) {
|
||||
throw new RuntimeException("Missing Moodle Webservice API functions: " + missingAPIFunctions);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final RuntimeException re) {
|
||||
|
@ -271,16 +276,19 @@ public class MoodleRestTemplateFactory {
|
|||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
|
@ -319,9 +327,7 @@ public class MoodleRestTemplateFactory {
|
|||
functionReqEntity,
|
||||
String.class);
|
||||
|
||||
final LmsSetup lmsSetup = MoodleRestTemplateFactory.this.apiTemplateDataSupplier
|
||||
.getLmsSetup();
|
||||
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
if (response.getStatusCode() != HttpStatus.OK) {
|
||||
throw new RuntimeException(
|
||||
"Failed to call Moodle webservice API function: " + functionName + " lms setup: " +
|
||||
|
@ -347,9 +353,7 @@ public class MoodleRestTemplateFactory {
|
|||
|
||||
private void requestAccessToken() {
|
||||
|
||||
final LmsSetup lmsSetup = MoodleRestTemplateFactory.this.apiTemplateDataSupplier
|
||||
.getLmsSetup();
|
||||
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
try {
|
||||
|
||||
final ResponseEntity<String> response = super.exchange(
|
||||
|
@ -369,7 +373,7 @@ public class MoodleRestTemplateFactory {
|
|||
}
|
||||
|
||||
try {
|
||||
final MoodleToken moodleToken = MoodleRestTemplateFactory.this.jsonMapper.readValue(
|
||||
final MoodleToken moodleToken = this.jsonMapper.readValue(
|
||||
response.getBody(),
|
||||
MoodleToken.class);
|
||||
|
|
@ -48,9 +48,11 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.APITemplateDataSupplier;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate.Warning;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader.CourseDataShort;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader.CourseQuizShort;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
|
||||
|
||||
/** Implements the LmsAPITemplate for Open edX LMS Course API access.
|
||||
*
|
||||
|
@ -885,40 +887,4 @@ public class MoodleCourseAccess implements CourseAccessAPI {
|
|||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static final class Warning {
|
||||
final String item;
|
||||
final String itemid;
|
||||
final String warningcode;
|
||||
final String message;
|
||||
|
||||
@JsonCreator
|
||||
public Warning(
|
||||
@JsonProperty(value = "item") final String item,
|
||||
@JsonProperty(value = "itemid") final String itemid,
|
||||
@JsonProperty(value = "warningcode") final String warningcode,
|
||||
@JsonProperty(value = "message") final String message) {
|
||||
|
||||
this.item = item;
|
||||
this.itemid = itemid;
|
||||
this.warningcode = warningcode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Warning [item=");
|
||||
builder.append(this.item);
|
||||
builder.append(", itemid=");
|
||||
builder.append(this.itemid);
|
||||
builder.append(", warningcode=");
|
||||
builder.append(this.warningcode);
|
||||
builder.append(", message=");
|
||||
builder.append(this.message);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,8 +47,8 @@ import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
|||
import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseAccess.Warning;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate.Warning;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
|
|
|
@ -30,7 +30,8 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
|||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionAPI;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
|
||||
/** GET:
|
||||
* http://yourmoodle.org/webservice/rest/server.php?wstoken={token}&moodlewsrestformat=json&wsfunction=seb_restriction&courseId=123
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle;
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
@ -28,10 +29,7 @@ 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.LmsAPITemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseAccess;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseRestriction;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCheck;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseAccess;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin.MoodlePluginCourseRestriction;
|
||||
|
@ -43,6 +41,7 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
|
||||
private final MoodlePluginCheck moodlePluginCheck;
|
||||
private final JSONMapper jsonMapper;
|
||||
private final CacheManager cacheManager;
|
||||
private final AsyncService asyncService;
|
||||
private final Environment environment;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
|
@ -53,6 +52,7 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
protected MoodleLmsAPITemplateFactory(
|
||||
final MoodlePluginCheck moodlePluginCheck,
|
||||
final JSONMapper jsonMapper,
|
||||
final CacheManager cacheManager,
|
||||
final AsyncService asyncService,
|
||||
final Environment environment,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
|
@ -62,6 +62,7 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
|
||||
this.moodlePluginCheck = moodlePluginCheck;
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.cacheManager = cacheManager;
|
||||
this.asyncService = asyncService;
|
||||
this.environment = environment;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
|
@ -87,9 +88,19 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
.getBean(MoodleCourseDataAsyncLoader.class);
|
||||
asyncLoaderPrototype.init(lmsSetup.getModelId());
|
||||
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactory(
|
||||
this.jsonMapper,
|
||||
apiTemplateDataSupplier,
|
||||
this.clientCredentialService,
|
||||
this.clientHttpRequestFactoryService,
|
||||
this.alternativeTokenRequestPaths);
|
||||
|
||||
if (this.moodlePluginCheck.checkPluginAvailable(lmsSetup)) {
|
||||
|
||||
final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess();
|
||||
final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess(
|
||||
this.jsonMapper,
|
||||
moodleRestTemplateFactory,
|
||||
this.cacheManager);
|
||||
final MoodlePluginCourseRestriction moodlePluginCourseRestriction = new MoodlePluginCourseRestriction();
|
||||
|
||||
return new LmsAPITemplateAdapter(
|
||||
|
@ -101,13 +112,6 @@ public class MoodleLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
|
||||
} else {
|
||||
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactory(
|
||||
this.jsonMapper,
|
||||
apiTemplateDataSupplier,
|
||||
this.clientCredentialService,
|
||||
this.clientHttpRequestFactoryService,
|
||||
this.alternativeTokenRequestPaths);
|
||||
|
||||
final MoodleCourseAccess moodleCourseAccess = new MoodleCourseAccess(
|
||||
this.jsonMapper,
|
||||
moodleRestTemplateFactory,
|
|
@ -8,30 +8,96 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
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.user.ExamineeAccountDetails;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.CourseAccessAPI;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleAPIRestTemplate.Warning;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
|
||||
public class MoodlePluginCourseAccess implements CourseAccessAPI {
|
||||
public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess implements CourseAccessAPI {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MoodlePluginCourseAccess.class);
|
||||
|
||||
static final String COURSES_API_FUNCTION_NAME = "local_sebserver_get_courses";
|
||||
static final String QUIZZES_BY_COURSES_API_FUNCTION_NAME = "local_sebserver_get_quizzes_by_courses";
|
||||
static final String USERS_API_FUNCTION_NAME = "local_sebserver_get_users";
|
||||
|
||||
static final String CRITERIA_FROM_DATE = "from_date";
|
||||
static final String CRITERIA_TO_DATE = "to_date";
|
||||
static final String CRITERIA_LIMIT_FROM = "limitfrom";
|
||||
static final String CRITERIA_LIMIT_NUM = "limitnum";
|
||||
|
||||
private final JSONMapper jsonMapper;
|
||||
private final MoodleRestTemplateFactory moodleRestTemplateFactory;
|
||||
|
||||
private MoodleAPIRestTemplate restTemplate;
|
||||
|
||||
public MoodlePluginCourseAccess(
|
||||
final JSONMapper jsonMapper,
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory,
|
||||
final CacheManager cacheManager) {
|
||||
super(cacheManager);
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.moodleRestTemplateFactory = moodleRestTemplateFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LmsSetupTestResult testCourseAccessAPI() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
final LmsSetupTestResult attributesCheck = this.moodleRestTemplateFactory.test();
|
||||
if (!attributesCheck.isOk()) {
|
||||
return attributesCheck;
|
||||
}
|
||||
|
||||
final Result<MoodleAPIRestTemplate> restTemplateRequest = getRestTemplate();
|
||||
if (restTemplateRequest.hasError()) {
|
||||
final String message = "Failed to gain access token from Moodle Rest API:\n tried token endpoints: " +
|
||||
this.moodleRestTemplateFactory.knownTokenAccessPaths;
|
||||
log.error(message + " cause: {}", restTemplateRequest.getError().getMessage());
|
||||
return LmsSetupTestResult.ofTokenRequestError(LmsType.MOODLE_PLUGIN, message);
|
||||
}
|
||||
|
||||
final MoodleAPIRestTemplate restTemplate = restTemplateRequest.get();
|
||||
|
||||
// try {
|
||||
// restTemplate.testAPIConnection(
|
||||
// COURSES_API_FUNCTION_NAME,
|
||||
// QUIZZES_BY_COURSES_API_FUNCTION_NAME,
|
||||
// USERS_API_FUNCTION_NAME);
|
||||
// } catch (final RuntimeException e) {
|
||||
// log.error("Failed to access Moodle course API: ", e);
|
||||
// return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.MOODLE_PLUGIN, e.getMessage());
|
||||
// }
|
||||
|
||||
return LmsSetupTestResult.ofOkay(LmsType.MOODLE_PLUGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<List<QuizData>> getQuizzes(final FilterMap filterMap) {
|
||||
System.out.println("***************** filterMap: " + filterMap);
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return Result.of(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,12 +112,6 @@ public class MoodlePluginCourseAccess implements CourseAccessAPI {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCourseCache() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<ExamineeAccountDetails> getExamineeAccountDetails(final String examineeUserId) {
|
||||
// TODO Auto-generated method stub
|
||||
|
@ -70,4 +130,119 @@ public class MoodlePluginCourseAccess implements CourseAccessAPI {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Long getLmsSetupId() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result<MoodleAPIRestTemplate> getRestTemplate() {
|
||||
if (this.restTemplate == null) {
|
||||
final Result<MoodleAPIRestTemplate> templateRequest = this.moodleRestTemplateFactory
|
||||
.createRestTemplate();
|
||||
if (templateRequest.hasError()) {
|
||||
return templateRequest;
|
||||
} else {
|
||||
this.restTemplate = templateRequest.get();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.of(this.restTemplate);
|
||||
}
|
||||
|
||||
// ---- Mapping Classes ---
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static final class Courses {
|
||||
final Collection<CourseData> courses;
|
||||
final Collection<Warning> warnings;
|
||||
|
||||
@JsonCreator
|
||||
protected Courses(
|
||||
@JsonProperty(value = "courses") final Collection<CourseData> courses,
|
||||
@JsonProperty(value = "warnings") final Collection<Warning> warnings) {
|
||||
this.courses = courses;
|
||||
this.warnings = warnings;
|
||||
}
|
||||
}
|
||||
|
||||
/** Maps the Moodle course API course data */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static final class CourseData {
|
||||
final String id;
|
||||
final String short_name;
|
||||
final String idnumber;
|
||||
final String full_name;
|
||||
final String display_name;
|
||||
final Long start_date; // unix-time seconds UTC
|
||||
final Long end_date; // unix-time seconds UTC
|
||||
final Long time_created; // unix-time seconds UTC
|
||||
final Collection<CourseQuiz> quizzes = new ArrayList<>();
|
||||
|
||||
@JsonCreator
|
||||
protected CourseData(
|
||||
@JsonProperty(value = "id") final String id,
|
||||
@JsonProperty(value = "shortname") final String short_name,
|
||||
@JsonProperty(value = "idnumber") final String idnumber,
|
||||
@JsonProperty(value = "fullname") final String full_name,
|
||||
@JsonProperty(value = "displayname") final String display_name,
|
||||
@JsonProperty(value = "startdate") final Long start_date,
|
||||
@JsonProperty(value = "enddate") final Long end_date,
|
||||
@JsonProperty(value = "timecreated") final Long time_created) {
|
||||
|
||||
this.id = id;
|
||||
this.short_name = short_name;
|
||||
this.idnumber = idnumber;
|
||||
this.full_name = full_name;
|
||||
this.display_name = display_name;
|
||||
this.start_date = start_date;
|
||||
this.end_date = end_date;
|
||||
this.time_created = time_created;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
private static final class CourseQuizData {
|
||||
final Collection<CourseQuiz> quizzes;
|
||||
final Collection<Warning> warnings;
|
||||
|
||||
@JsonCreator
|
||||
protected CourseQuizData(
|
||||
@JsonProperty(value = "quizzes") final Collection<CourseQuiz> quizzes,
|
||||
@JsonProperty(value = "warnings") final Collection<Warning> warnings) {
|
||||
this.quizzes = quizzes;
|
||||
this.warnings = warnings;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static final class CourseQuiz {
|
||||
final String id;
|
||||
final String course;
|
||||
final String course_module;
|
||||
final String name;
|
||||
final String intro; // HTML
|
||||
final Long time_open; // unix-time seconds UTC
|
||||
final Long time_close; // unix-time seconds UTC
|
||||
|
||||
@JsonCreator
|
||||
protected CourseQuiz(
|
||||
@JsonProperty(value = "id") final String id,
|
||||
@JsonProperty(value = "course") final String course,
|
||||
@JsonProperty(value = "coursemodule") final String course_module,
|
||||
@JsonProperty(value = "name") final String name,
|
||||
@JsonProperty(value = "intro") final String intro,
|
||||
@JsonProperty(value = "timeopen") final Long time_open,
|
||||
@JsonProperty(value = "timeclose") final Long time_close) {
|
||||
|
||||
this.id = id;
|
||||
this.course = course;
|
||||
this.course_module = course_module;
|
||||
this.name = name;
|
||||
this.intro = intro;
|
||||
this.time_open = time_open;
|
||||
this.time_close = time_close;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction;
|
||||
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.webservice.servicelayer.lms.SEBRestrictionAPI;
|
||||
|
@ -19,7 +20,7 @@ public class MoodlePluginCourseRestriction implements SEBRestrictionAPI {
|
|||
@Override
|
||||
public LmsSetupTestResult testCourseRestrictionAPI() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return LmsSetupTestResult.ofOkay(LmsType.MOODLE_PLUGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
||||
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.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
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.LmsAPITemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.LmsAPITemplateAdapter;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleCourseDataAsyncLoader;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
@WebServiceProfile
|
||||
public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
||||
|
||||
private final MoodlePluginCheck moodlePluginCheck;
|
||||
private final JSONMapper jsonMapper;
|
||||
private final CacheManager cacheManager;
|
||||
private final AsyncService asyncService;
|
||||
private final Environment environment;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
|
||||
private final ApplicationContext applicationContext;
|
||||
private final String[] alternativeTokenRequestPaths;
|
||||
|
||||
protected MooldePluginLmsAPITemplateFactory(
|
||||
final MoodlePluginCheck moodlePluginCheck,
|
||||
final JSONMapper jsonMapper,
|
||||
final CacheManager cacheManager,
|
||||
final AsyncService asyncService,
|
||||
final Environment environment,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
||||
final ApplicationContext applicationContext,
|
||||
@Value("${sebserver.webservice.lms.moodle.api.token.request.paths:}") final String alternativeTokenRequestPaths) {
|
||||
|
||||
this.moodlePluginCheck = moodlePluginCheck;
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.cacheManager = cacheManager;
|
||||
this.asyncService = asyncService;
|
||||
this.environment = environment;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
|
||||
this.applicationContext = applicationContext;
|
||||
this.alternativeTokenRequestPaths = (alternativeTokenRequestPaths != null)
|
||||
? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LmsType lmsType() {
|
||||
return LmsType.MOODLE_PLUGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<LmsAPITemplate> create(final APITemplateDataSupplier apiTemplateDataSupplier) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
final LmsSetup lmsSetup = apiTemplateDataSupplier.getLmsSetup();
|
||||
final MoodleCourseDataAsyncLoader asyncLoaderPrototype = this.applicationContext
|
||||
.getBean(MoodleCourseDataAsyncLoader.class);
|
||||
asyncLoaderPrototype.init(lmsSetup.getModelId());
|
||||
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = new MoodleRestTemplateFactory(
|
||||
this.jsonMapper,
|
||||
apiTemplateDataSupplier,
|
||||
this.clientCredentialService,
|
||||
this.clientHttpRequestFactoryService,
|
||||
this.alternativeTokenRequestPaths);
|
||||
|
||||
final MoodlePluginCourseAccess moodlePluginCourseAccess = new MoodlePluginCourseAccess(
|
||||
this.jsonMapper,
|
||||
moodleRestTemplateFactory,
|
||||
this.cacheManager);
|
||||
|
||||
final MoodlePluginCourseRestriction moodlePluginCourseRestriction = new MoodlePluginCourseRestriction();
|
||||
|
||||
return new LmsAPITemplateAdapter(
|
||||
this.asyncService,
|
||||
this.environment,
|
||||
apiTemplateDataSupplier,
|
||||
moodlePluginCourseAccess,
|
||||
moodlePluginCourseRestriction);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,8 @@ 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.model.user.ExamineeAccountDetails;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplateImpl;
|
||||
|
||||
public class MoodleCourseAccessTest {
|
||||
|
||||
|
@ -38,7 +39,7 @@ public class MoodleCourseAccessTest {
|
|||
public void testGetExamineeAccountDetails() {
|
||||
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = mock(MoodleRestTemplateFactory.class);
|
||||
final MoodleAPIRestTemplate moodleAPIRestTemplate = mock(MoodleAPIRestTemplate.class);
|
||||
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
|
||||
when(moodleRestTemplateFactory.createRestTemplate()).thenReturn(Result.of(moodleAPIRestTemplate));
|
||||
when(moodleAPIRestTemplate.callMoodleAPIFunction(
|
||||
anyString(),
|
||||
|
@ -132,7 +133,7 @@ public class MoodleCourseAccessTest {
|
|||
@Test
|
||||
public void testInitAPIAccessError2() {
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = mock(MoodleRestTemplateFactory.class);
|
||||
final MoodleAPIRestTemplate moodleAPIRestTemplate = mock(MoodleAPIRestTemplate.class);
|
||||
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
|
||||
when(moodleRestTemplateFactory.createRestTemplate()).thenReturn(Result.of(moodleAPIRestTemplate));
|
||||
doThrow(RuntimeException.class).when(moodleAPIRestTemplate).testAPIConnection(any());
|
||||
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay(LmsType.MOODLE));
|
||||
|
@ -153,7 +154,7 @@ public class MoodleCourseAccessTest {
|
|||
@Test
|
||||
public void testInitAPIAccessOK() {
|
||||
final MoodleRestTemplateFactory moodleRestTemplateFactory = mock(MoodleRestTemplateFactory.class);
|
||||
final MoodleAPIRestTemplate moodleAPIRestTemplate = mock(MoodleAPIRestTemplate.class);
|
||||
final MoodleAPIRestTemplateImpl moodleAPIRestTemplate = mock(MoodleAPIRestTemplateImpl.class);
|
||||
when(moodleRestTemplateFactory.createRestTemplate()).thenReturn(Result.of(moodleAPIRestTemplate));
|
||||
when(moodleRestTemplateFactory.test()).thenReturn(LmsSetupTestResult.ofOkay(LmsType.MOODLE));
|
||||
|
||||
|
|
Loading…
Reference in a new issue