Merge remote-tracking branch 'origin/dev-lms-open-olat' into dev-1.2
This commit is contained in:
commit
cbbff94a75
5 changed files with 108 additions and 45 deletions
|
@ -63,7 +63,7 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
|||
/** The Ans Delft binding is on the way */
|
||||
ANS_DELFT(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||
/** The OpenOLAT binding is on the way */
|
||||
OPEN_OLAT(/* Features.COURSE_API , Features.SEB_RESTRICTION */);
|
||||
OPEN_OLAT(Features.COURSE_API, Features.SEB_RESTRICTION);
|
||||
|
||||
public final EnumSet<Features> features;
|
||||
|
||||
|
|
|
@ -20,10 +20,16 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.joda.time.DateTime;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
|
||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||
|
||||
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentials;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ProxyData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
|
@ -45,6 +51,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCour
|
|||
public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate {
|
||||
|
||||
// TODO add needed dependencies here
|
||||
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
private final APITemplateDataSupplier apiTemplateDataSupplier;
|
||||
private final Long lmsSetupId;
|
||||
|
||||
|
@ -52,6 +60,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
|
||||
// TODO if you need more dependencies inject them here and set the reference
|
||||
|
||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
final APITemplateDataSupplier apiTemplateDataSupplier,
|
||||
final AsyncService asyncService,
|
||||
final Environment environment,
|
||||
|
@ -59,6 +69,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
|
||||
super(asyncService, environment, cacheManager);
|
||||
|
||||
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
this.apiTemplateDataSupplier = apiTemplateDataSupplier;
|
||||
this.lmsSetupId = apiTemplateDataSupplier.getLmsSetup().id;
|
||||
}
|
||||
|
@ -83,12 +95,15 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings();
|
||||
if (testLmsSetupSettings.hasAnyError()) {
|
||||
return testLmsSetupSettings;
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
// TODO check if the course API of the remote LMS is available
|
||||
// if not, create corresponding LmsSetupTestResult error
|
||||
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.OPEN_OLAT, "TODO: implement LMS access check");
|
||||
|
||||
return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT);
|
||||
//return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,14 +208,20 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
@Override
|
||||
protected Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap) {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final String quizName = filterMap.getString(QuizData.FILTER_ATTR_QUIZ_NAME);
|
||||
@SuppressWarnings("unused")
|
||||
final DateTime quizFromTime = (filterMap != null) ? filterMap.getQuizFromTime() : null;
|
||||
|
||||
// TODO get all course / quiz data from remote LMS that matches the filter criteria.
|
||||
// put loaded QuizData to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
return () -> {
|
||||
|
||||
// TODO Get all course / quiz data from remote LMS that matches the filter criteria.
|
||||
// If the LMS API uses paging, go through all pages using the filter criteria
|
||||
// and collect the course data.
|
||||
// Transform the data from courses / quizzes from LMS into QuizData objects
|
||||
// Put loaded QuizData objects to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
};
|
||||
}
|
||||
|
@ -208,11 +229,13 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
@Override
|
||||
protected Supplier<Collection<QuizData>> quizzesSupplier(final Set<String> ids) {
|
||||
|
||||
// TODO get all quiz / course data for specified identifiers from remote LMS
|
||||
// and put it to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
return () -> {
|
||||
|
||||
// TODO get all quiz / course data for specified identifiers from remote LMS
|
||||
// Transform the data from courses / quizzes from LMS into QuizData objects
|
||||
// and put it to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
};
|
||||
}
|
||||
|
@ -220,11 +243,12 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
@Override
|
||||
protected Supplier<QuizData> quizSupplier(final String id) {
|
||||
|
||||
// TODO get the specified quiz / course data for specified identifier from remote LMS
|
||||
// and put it to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
return () -> {
|
||||
|
||||
// TODO get the specified quiz / course data for specified identifier from remote LMS
|
||||
// and put it to the cache: super.putToCache(quizDataCollection);
|
||||
// before returning it.
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
};
|
||||
}
|
||||
|
@ -232,10 +256,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
@Override
|
||||
protected Supplier<ExamineeAccountDetails> accountDetailsSupplier(final String examineeSessionId) {
|
||||
|
||||
// TODO get the examinee's account details by the given examineeSessionId from remote LMS.
|
||||
// Currently only the name is needed to display on monitoring view.
|
||||
|
||||
return () -> {
|
||||
|
||||
// TODO get the examinee's account details by the given examineeSessionId from remote LMS.
|
||||
// Currently only the name is needed to display on monitoring view.
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
};
|
||||
}
|
||||
|
@ -243,19 +268,22 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
@Override
|
||||
protected Supplier<Chapters> getCourseChaptersSupplier(final String courseId) {
|
||||
return () -> {
|
||||
throw new UnsupportedOperationException("not available yet");
|
||||
throw new UnsupportedOperationException("No Course Chapter available for OpenOLAT LMS");
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SEBRestriction> getSEBClientRestriction(final Exam exam) {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final String quizId = exam.externalId;
|
||||
|
||||
// TODO get the SEB client restrictions that are currently set on the remote LMS for
|
||||
// the given quiz / course derived from the given exam
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
return Result.ofRuntimeError("TODO");
|
||||
// TODO get the SEB client restrictions that are currently set on the remote LMS for
|
||||
// the given quiz / course derived from the given exam
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -263,22 +291,59 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
final String externalExamId,
|
||||
final SEBRestriction sebRestrictionData) {
|
||||
|
||||
// TODO apply the given sebRestrictionData settings as current SEB client restriction setting
|
||||
// to the remote LMS for the given quiz / course.
|
||||
// Mainly SEBRestriction.configKeys and SEBRestriction.browserExamKeys
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
return Result.ofRuntimeError("TODO");
|
||||
// TODO apply the given sebRestrictionData settings as current SEB client restriction setting
|
||||
// to the remote LMS for the given quiz / course.
|
||||
// Mainly SEBRestriction.configKeys and SEBRestriction.browserExamKeys
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> releaseSEBClientRestriction(final Exam exam) {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
final String quizId = exam.externalId;
|
||||
|
||||
// TODO Release respectively delete all SEB client restrictions for the given
|
||||
// course / quize on the remote LMS.
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
return Result.ofRuntimeError("TODO");
|
||||
// TODO Release respectively delete all SEB client restrictions for the given
|
||||
// course / quize on the remote LMS.
|
||||
|
||||
throw new RuntimeException("TODO");
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: This is an example of how to create a RestTemplate for the service to access the LMS API
|
||||
// The example deals with a Http based API that is secured by an OAuth2 client-credential flow.
|
||||
// You might need some different template, then you have to adapt this code
|
||||
// To your needs.
|
||||
@SuppressWarnings("unused")
|
||||
private OAuth2RestTemplate createRestTemplate(final String accessTokenRequestPath) {
|
||||
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials();
|
||||
final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData();
|
||||
|
||||
final CharSequence plainClientId = credentials.clientId;
|
||||
final CharSequence plainClientSecret = this.clientCredentialService
|
||||
.getPlainClientSecret(credentials)
|
||||
.getOrThrow();
|
||||
|
||||
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
||||
details.setAccessTokenUri(lmsSetup.lmsApiUrl + accessTokenRequestPath);
|
||||
details.setClientId(plainClientId.toString());
|
||||
details.setClientSecret(plainClientSecret.toString());
|
||||
|
||||
final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService
|
||||
.getClientHttpRequestFactory(proxyData)
|
||||
.getOrThrow();
|
||||
|
||||
final OAuth2RestTemplate template = new OAuth2RestTemplate(details);
|
||||
template.setRequestFactory(clientHttpRequestFactory);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ 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.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService;
|
||||
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;
|
||||
|
@ -33,15 +35,21 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplateFactory;
|
|||
* as usual. Just add the additionally needed dependencies used to build a OlatLmsAPITemplate. */
|
||||
public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
||||
|
||||
private final ClientHttpRequestFactoryService clientHttpRequestFactoryService;
|
||||
private final ClientCredentialService clientCredentialService;
|
||||
private final AsyncService asyncService;
|
||||
private final Environment environment;
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
public OlatLmsAPITemplateFactory(
|
||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
||||
final ClientCredentialService clientCredentialService,
|
||||
final AsyncService asyncService,
|
||||
final Environment environment,
|
||||
final CacheManager cacheManager) {
|
||||
|
||||
this.clientHttpRequestFactoryService = clientHttpRequestFactoryService;
|
||||
this.clientCredentialService = clientCredentialService;
|
||||
this.asyncService = asyncService;
|
||||
this.environment = environment;
|
||||
this.cacheManager = cacheManager;
|
||||
|
@ -56,6 +64,8 @@ public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory {
|
|||
public Result<LmsAPITemplate> create(final APITemplateDataSupplier apiTemplateDataSupplier) {
|
||||
return Result.tryCatch(() -> {
|
||||
return new OlatLmsAPITemplate(
|
||||
this.clientHttpRequestFactoryService,
|
||||
this.clientCredentialService,
|
||||
apiTemplateDataSupplier,
|
||||
this.asyncService,
|
||||
this.environment,
|
||||
|
|
|
@ -51,21 +51,6 @@ sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
|
|||
sebserver.webservice.lms.moodle.api.token.request.paths=
|
||||
sebserver.webservice.lms.address.alias=lms.mockup.com=lms.address.alias
|
||||
|
||||
# NOTE: This is a temporary work-around for SEB Restriction API within Open edX SEB integration plugin to
|
||||
# apply on load-balanced infrastructure or infrastructure that has several layers of cache.
|
||||
# The reason for this is that the API (Open edX system) internally don't apply a resource-change that is
|
||||
# done within HTTP API call immediately from an outside perspective.
|
||||
# After a resource-change on the API is done, the system toggles between the old and the new resource
|
||||
# while constantly calling GET. This usually happens for about a minute or two then it stabilizes on the new resource
|
||||
#
|
||||
# This may source on load-balancing or internally caching on Open edX side.
|
||||
# To mitigate this effect the SEB Server can be configured to apply a resource-change on the
|
||||
# API several times in a row to flush as match caches and reach as match as possible server instances.
|
||||
#
|
||||
# Since this is a brute-force method to mitigate the problem, this should only be a temporary
|
||||
# work-around until a better solution on Open edX SEB integration side has been found and applied.
|
||||
#sebserver.webservice.lms.openedx.seb.restriction.push-count=10
|
||||
|
||||
# actuator configuration
|
||||
management.server.port=${server.port}
|
||||
management.endpoints.web.base-path=/management
|
||||
|
|
|
@ -12,6 +12,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
@ -28,6 +29,8 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
|||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
|
||||
// NOTE this test seems sometimes not to work (maybe a ordering problem)
|
||||
@Ignore
|
||||
public class ExamImportTest extends AdministrationAPIIntegrationTester {
|
||||
|
||||
@Autowired
|
||||
|
|
Loading…
Reference in a new issue