Merge remote-tracking branch 'origin/dev-lms-open-olat' into development
This commit is contained in:
		
						commit
						54a2dfd7d8
					
				
					 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); | ||||
|         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. | ||||
| 
 | ||||
|         return () -> { | ||||
|             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) { | ||||
| 
 | ||||
|         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. | ||||
| 
 | ||||
|         return () -> { | ||||
|             throw new RuntimeException("TODO"); | ||||
|         }; | ||||
|     } | ||||
|  | @ -220,11 +243,12 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm | |||
|     @Override | ||||
|     protected Supplier<QuizData> quizSupplier(final String id) { | ||||
| 
 | ||||
|         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. | ||||
| 
 | ||||
|         return () -> { | ||||
|             throw new RuntimeException("TODO"); | ||||
|         }; | ||||
|     } | ||||
|  | @ -232,10 +256,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm | |||
|     @Override | ||||
|     protected Supplier<ExamineeAccountDetails> accountDetailsSupplier(final String examineeSessionId) { | ||||
| 
 | ||||
|         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. | ||||
| 
 | ||||
|         return () -> { | ||||
|             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; | ||||
| 
 | ||||
|         return Result.tryCatch(() -> { | ||||
| 
 | ||||
|             // 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.ofRuntimeError("TODO"); | ||||
|             throw new RuntimeException("TODO"); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -263,22 +291,59 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm | |||
|             final String externalExamId, | ||||
|             final SEBRestriction sebRestrictionData) { | ||||
| 
 | ||||
|         return Result.tryCatch(() -> { | ||||
| 
 | ||||
|             // 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.ofRuntimeError("TODO"); | ||||
|             throw new RuntimeException("TODO"); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Result<Exam> releaseSEBClientRestriction(final Exam exam) { | ||||
| 
 | ||||
|         @SuppressWarnings("unused") | ||||
|         final String quizId = exam.externalId; | ||||
| 
 | ||||
|         return Result.tryCatch(() -> { | ||||
| 
 | ||||
|             // TODO Release respectively delete all SEB client restrictions for the given | ||||
|             //      course / quize on the remote LMS. | ||||
| 
 | ||||
|         return Result.ofRuntimeError("TODO"); | ||||
|             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…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti