From 89c306e35ad5b5707a8766605ce8776ba9786b84 Mon Sep 17 00:00:00 2001 From: anhefti Date: Mon, 17 May 2021 22:43:42 +0200 Subject: [PATCH] added OlatLmsAPITemplate skeleton ready to be implemented --- .../seb/sebserver/gbl/async/AsyncService.java | 12 +- .../lms/impl/AbstractCourseAccess.java | 24 +- .../lms/impl/edx/OpenEdxCourseAccess.java | 86 +++--- .../lms/impl/moodle/MoodleCourseAccess.java | 102 ++++--- .../lms/impl/olat/OlatLmsAPITemplate.java | 282 ++++++++++++++++++ .../impl/olat/OlatLmsAPITemplateFactory.java | 37 +++ .../impl/moodle/MoodleCourseAccessTest.java | 3 +- 7 files changed, 430 insertions(+), 116 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java index 391dbbc8..17f9f816 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/async/AsyncService.java @@ -15,19 +15,19 @@ import org.springframework.stereotype.Service; @Lazy @Service -/** Implements a asynchronous service to manly support CircuitBreaker and MemoizingCircuitBreaker functionality. */ +/** Implements a asynchronous service to manly support CircuitBreaker and MemoizingCircuitBreaker functionality. */ public class AsyncService { private final AsyncRunner asyncRunner; - protected AsyncService(final AsyncRunner asyncRunner) { + public AsyncService(final AsyncRunner asyncRunner) { this.asyncRunner = asyncRunner; } /** Create a CircuitBreaker of specified type with the default parameter defined in the CircuitBreaker class * * @param the type of the CircuitBreaker - * @return a CircuitBreaker of specified type with the default parameter defined in the CircuitBreaker class */ + * @return a CircuitBreaker of specified type with the default parameter defined in the CircuitBreaker class */ public CircuitBreaker createCircuitBreaker() { return new CircuitBreaker<>(this.asyncRunner); } @@ -38,7 +38,7 @@ public class AsyncService { * @param maxBlockingTime maximal time since call CircuitBreaker waits for a response before going onto open state. * @param timeToRecover the time the CircuitBreaker takes to recover form open state. * @param the type of the CircuitBreaker - * @return a CircuitBreaker of specified type */ + * @return a CircuitBreaker of specified type */ public CircuitBreaker createCircuitBreaker( final int maxFailingAttempts, final long maxBlockingTime, @@ -52,7 +52,7 @@ public class AsyncService { } /** Create a MemoizingCircuitBreaker of specified type that memoize a successful result and return the last - * successful result on fail as long as maxMemoizingTime is not exceeded. + * successful result on fail as long as maxMemoizingTime is not exceeded. * * @param blockingSupplier the blocking result supplier that the MemoizingCircuitBreaker must call * @param maxFailingAttempts maximal number of attempts the CircuitBreaker allows before going onto open state. @@ -61,7 +61,7 @@ public class AsyncService { * @param momoized whether the memoizing functionality is on or off * @param maxMemoizingTime the maximal time memorized data is valid * @param the type of the CircuitBreaker - * @return a CircuitBreaker of specified type */ + * @return a CircuitBreaker of specified type */ public MemoizingCircuitBreaker createMemoizingCircuitBreaker( final Supplier blockingSupplier, final int maxFailingAttempts, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java index 2853abdd..b663ae96 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/AbstractCourseAccess.java @@ -9,7 +9,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.Supplier; @@ -134,10 +133,8 @@ public abstract class AbstractCourseAccess { return this.allQuizzesRequest.protectedRun(allQuizzesSupplier(filterMap)); } - public Collection protectedQuizzesRequest(final Set ids) { - return this.quizzesRequest.protectedRun(quizzesSupplier(ids)) - .onError(error -> log.error("Failed to get QuizData for ids: ", error)) - .getOrElse(() -> Collections.emptyList()); + public Result> protectedQuizzesRequest(final Set ids) { + return this.quizzesRequest.protectedRun(quizzesSupplier(ids)); } public Result protectedQuizRequest(final String id) { @@ -145,7 +142,8 @@ public abstract class AbstractCourseAccess { } public Result getExamineeAccountDetails(final String examineeSessionId) { - return this.accountDetailRequest.protectedRun(accountDetailsSupplier(examineeSessionId)); + final Supplier accountDetailsSupplier = accountDetailsSupplier(examineeSessionId); + return this.accountDetailRequest.protectedRun(accountDetailsSupplier); } /** Default implementation that uses getExamineeAccountDetails to geht the examinee name @@ -163,19 +161,7 @@ public abstract class AbstractCourseAccess { return this.chaptersRequest.protectedRun(getCourseChaptersSupplier(courseId)); } - /** NOTE: this returns a ExamineeAccountDetails with given examineeSessionId for default. - * Override this if requesting account details is supported for specified LMS access. - * - * @param examineeSessionId - * @return this returns a ExamineeAccountDetails with given examineeSessionId for default */ - protected Supplier accountDetailsSupplier(final String examineeSessionId) { - return () -> new ExamineeAccountDetails( - examineeSessionId, - examineeSessionId, - examineeSessionId, - examineeSessionId, - Collections.emptyMap()); - } + protected abstract Supplier accountDetailsSupplier(final String examineeSessionId); /** Provides a supplier to supply request to use within the circuit breaker */ protected abstract Supplier> allQuizzesSupplier(final FilterMap filterMap); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java index 71f87985..fd4165b6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/edx/OpenEdxCourseAccess.java @@ -141,52 +141,56 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { } @Override - public Result getExamineeAccountDetails(final String examineeSessionId) { - return Result.tryCatch(() -> { - final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); - final HttpHeaders httpHeaders = new HttpHeaders(); - final OAuth2RestTemplate template = getRestTemplate() - .getOrThrow(); + protected Supplier accountDetailsSupplier(final String examineeSessionId) { + return () -> { + try { + final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); + final HttpHeaders httpHeaders = new HttpHeaders(); + final OAuth2RestTemplate template = getRestTemplate() + .getOrThrow(); - final String externalStartURI = this.webserviceInfo - .getLmsExternalAddressAlias(lmsSetup.lmsApiUrl); + final String externalStartURI = this.webserviceInfo + .getLmsExternalAddressAlias(lmsSetup.lmsApiUrl); - final String uri = (externalStartURI != null) - ? externalStartURI + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId - : lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId; + final String uri = (externalStartURI != null) + ? externalStartURI + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId + : lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_USER_PROFILE_ENDPOINT + examineeSessionId; - final String responseJSON = template.exchange( - uri, - HttpMethod.GET, - new HttpEntity<>(httpHeaders), - String.class) - .getBody(); + final String responseJSON = template.exchange( + uri, + HttpMethod.GET, + new HttpEntity<>(httpHeaders), + String.class) + .getBody(); - final EdxUserDetails[] userDetails = this.jsonMapper. readValue( - responseJSON, - new TypeReference() { - }); + final EdxUserDetails[] userDetails = this.jsonMapper. readValue( + responseJSON, + new TypeReference() { + }); - if (userDetails == null || userDetails.length <= 0) { - throw new RuntimeException("No user details on Open edX API request"); + if (userDetails == null || userDetails.length <= 0) { + throw new RuntimeException("No user details on Open edX API request"); + } + + final Map additionalAttributes = new HashMap<>(); + additionalAttributes.put("bio", userDetails[0].bio); + additionalAttributes.put("country", userDetails[0].country); + additionalAttributes.put("date_joined", userDetails[0].date_joined); + additionalAttributes.put("gender", userDetails[0].gender); + additionalAttributes.put("is_active", String.valueOf(userDetails[0].is_active)); + additionalAttributes.put("mailing_address", userDetails[0].mailing_address); + additionalAttributes.put("secondary_email", userDetails[0].secondary_email); + + return new ExamineeAccountDetails( + userDetails[0].username, + userDetails[0].name, + userDetails[0].username, + userDetails[0].email, + additionalAttributes); + } catch (final Exception e) { + throw new RuntimeException(e); } - - final Map additionalAttributes = new HashMap<>(); - additionalAttributes.put("bio", userDetails[0].bio); - additionalAttributes.put("country", userDetails[0].country); - additionalAttributes.put("date_joined", userDetails[0].date_joined); - additionalAttributes.put("gender", userDetails[0].gender); - additionalAttributes.put("is_active", String.valueOf(userDetails[0].is_active)); - additionalAttributes.put("mailing_address", userDetails[0].mailing_address); - additionalAttributes.put("secondary_email", userDetails[0].secondary_email); - - return new ExamineeAccountDetails( - userDetails[0].username, - userDetails[0].name, - userDetails[0].username, - userDetails[0].email, - additionalAttributes); - }); + }; } @Override @@ -275,7 +279,7 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess { }); if (!leftIds.isEmpty()) { - result.addAll(super.protectedQuizzesRequest(leftIds)); + result.addAll(super.protectedQuizzesRequest(leftIds).getOrThrow()); } return result; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java index a0fb86ff..d1da4849 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccess.java @@ -129,59 +129,63 @@ public class MoodleCourseAccess extends AbstractCourseAccess { } @Override - public Result getExamineeAccountDetails(final String examineeSessionId) { - return Result.tryCatch(() -> { - final MoodleAPIRestTemplate template = getRestTemplate() - .getOrThrow(); + protected Supplier accountDetailsSupplier(final String examineeSessionId) { + return () -> { + try { + final MoodleAPIRestTemplate template = getRestTemplate() + .getOrThrow(); - final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); - queryAttributes.add("field", "id"); - queryAttributes.add("values[0]", examineeSessionId); + final MultiValueMap queryAttributes = new LinkedMultiValueMap<>(); + queryAttributes.add("field", "id"); + queryAttributes.add("values[0]", examineeSessionId); - final String userDetailsJSON = template.callMoodleAPIFunction( - MOODLE_USER_PROFILE_API_FUNCTION_NAME, - queryAttributes); - - if (checkAccessDeniedError(userDetailsJSON)) { - final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); - log.error("Get access denied error from Moodle: {} for API call: {}, response: {}", - lmsSetup, + final String userDetailsJSON = template.callMoodleAPIFunction( MOODLE_USER_PROFILE_API_FUNCTION_NAME, - Utils.truncateText(userDetailsJSON, 2000)); - throw new RuntimeException("No user details on Moodle API request (access-denied)"); + queryAttributes); + + if (checkAccessDeniedError(userDetailsJSON)) { + final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup(); + log.error("Get access denied error from Moodle: {} for API call: {}, response: {}", + lmsSetup, + MOODLE_USER_PROFILE_API_FUNCTION_NAME, + Utils.truncateText(userDetailsJSON, 2000)); + throw new RuntimeException("No user details on Moodle API request (access-denied)"); + } + + final MoodleUserDetails[] userDetails = this.jsonMapper. readValue( + userDetailsJSON, + new TypeReference() { + }); + + if (userDetails == null || userDetails.length <= 0) { + throw new RuntimeException("No user details on Moodle API request"); + } + + final Map additionalAttributes = new HashMap<>(); + additionalAttributes.put("firstname", userDetails[0].firstname); + additionalAttributes.put("lastname", userDetails[0].lastname); + additionalAttributes.put("department", userDetails[0].department); + additionalAttributes.put("firstaccess", String.valueOf(userDetails[0].firstaccess)); + additionalAttributes.put("lastaccess", String.valueOf(userDetails[0].lastaccess)); + additionalAttributes.put("auth", userDetails[0].auth); + additionalAttributes.put("suspended", String.valueOf(userDetails[0].suspended)); + additionalAttributes.put("confirmed", String.valueOf(userDetails[0].confirmed)); + additionalAttributes.put("lang", userDetails[0].lang); + additionalAttributes.put("theme", userDetails[0].theme); + additionalAttributes.put("timezone", userDetails[0].timezone); + additionalAttributes.put("description", userDetails[0].description); + additionalAttributes.put("mailformat", String.valueOf(userDetails[0].mailformat)); + additionalAttributes.put("descriptionformat", String.valueOf(userDetails[0].descriptionformat)); + return new ExamineeAccountDetails( + userDetails[0].id, + userDetails[0].fullname, + userDetails[0].username, + userDetails[0].email, + additionalAttributes); + } catch (final Exception e) { + throw new RuntimeException(e); } - - final MoodleUserDetails[] userDetails = this.jsonMapper. readValue( - userDetailsJSON, - new TypeReference() { - }); - - if (userDetails == null || userDetails.length <= 0) { - throw new RuntimeException("No user details on Moodle API request"); - } - - final Map additionalAttributes = new HashMap<>(); - additionalAttributes.put("firstname", userDetails[0].firstname); - additionalAttributes.put("lastname", userDetails[0].lastname); - additionalAttributes.put("department", userDetails[0].department); - additionalAttributes.put("firstaccess", String.valueOf(userDetails[0].firstaccess)); - additionalAttributes.put("lastaccess", String.valueOf(userDetails[0].lastaccess)); - additionalAttributes.put("auth", userDetails[0].auth); - additionalAttributes.put("suspended", String.valueOf(userDetails[0].suspended)); - additionalAttributes.put("confirmed", String.valueOf(userDetails[0].confirmed)); - additionalAttributes.put("lang", userDetails[0].lang); - additionalAttributes.put("theme", userDetails[0].theme); - additionalAttributes.put("timezone", userDetails[0].timezone); - additionalAttributes.put("description", userDetails[0].description); - additionalAttributes.put("mailformat", String.valueOf(userDetails[0].mailformat)); - additionalAttributes.put("descriptionformat", String.valueOf(userDetails[0].descriptionformat)); - return new ExamineeAccountDetails( - userDetails[0].id, - userDetails[0].fullname, - userDetails[0].username, - userDetails[0].email, - additionalAttributes); - }); + }; } LmsSetupTestResult initAPIAccess() { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java new file mode 100644 index 00000000..e6a45dcd --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2021 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.olat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.cache.CacheManager; +import org.springframework.core.env.Environment; + +import ch.ethz.seb.sebserver.gbl.api.APIMessage; +import ch.ethz.seb.sebserver.gbl.async.AsyncService; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentials; +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; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; +import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features; +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.gbl.util.Utils; +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.LmsAPIService; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; +import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess; + +public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate { + + // TODO add needed dependencies here + private final APITemplateDataSupplier apiTemplateDataSupplier; + private final Long lmsSetupId; + + protected OlatLmsAPITemplate( + // TODO if you need more dependencies inject them here and set the reference + final APITemplateDataSupplier apiTemplateDataSupplier, + final AsyncService asyncService, + final Environment environment, + final CacheManager cacheManager) { + + super(asyncService, environment, cacheManager); + + this.apiTemplateDataSupplier = apiTemplateDataSupplier; + this.lmsSetupId = apiTemplateDataSupplier.getLmsSetup().id; + } + + @Override + public LmsType getType() { + return LmsType.OPEN_OLAT; + } + + @Override + public LmsSetup lmsSetup() { + return this.apiTemplateDataSupplier.getLmsSetup(); + } + + @Override + protected Long getLmsSetupId() { + return this.lmsSetupId; + } + + @Override + public LmsSetupTestResult testCourseAccessAPI() { + final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings(); + if (testLmsSetupSettings.hasAnyError()) { + return testLmsSetupSettings; + } + + // TODO check if the course API of the remote LMS is available + // if not, create corresponding LmsSetupTestResult error + + return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT); + } + + @Override + public LmsSetupTestResult testCourseRestrictionAPI() { + final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings(); + if (testLmsSetupSettings.hasAnyError()) { + return testLmsSetupSettings; + } + + if (LmsType.OPEN_OLAT.features.contains(Features.SEB_RESTRICTION)) { + + // TODO check if the course API of the remote LMS is available + // if not, create corresponding LmsSetupTestResult error + + } + + return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT); + } + + private LmsSetupTestResult testLmsSetupSettings() { + + final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); + final ClientCredentials lmsClientCredentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); + final List missingAttrs = new ArrayList<>(); + + // Check given LMS URL + if (StringUtils.isBlank(lmsSetup.lmsApiUrl)) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_URL, + "lmsSetup:lmsUrl:notNull")); + } else { + // Try to connect to the URL + if (!Utils.pingHost(lmsSetup.lmsApiUrl)) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_URL, + "lmsSetup:lmsUrl:url.invalid")); + } + } + + // Client id is mandatory + if (!lmsClientCredentials.hasClientId()) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_CLIENTNAME, + "lmsSetup:lmsClientname:notNull")); + } + + // Client secret is mandatory + if (!lmsClientCredentials.hasSecret()) { + missingAttrs.add(APIMessage.fieldValidationError( + LMS_SETUP.ATTR_LMS_CLIENTSECRET, + "lmsSetup:lmsClientsecret:notNull")); + } + + if (!missingAttrs.isEmpty()) { + return LmsSetupTestResult.ofMissingAttributes(LmsType.OPEN_OLAT, missingAttrs); + } + + return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT); + } + + @Override + public Result> getQuizzes(final FilterMap filterMap) { + return this + .protectedQuizzesRequest(filterMap) + .map(quizzes -> quizzes.stream() + .filter(LmsAPIService.quizFilterPredicate(filterMap)) + .collect(Collectors.toList())); + } + + @Override + public Result> getQuizzes(final Set ids) { + return Result.tryCatch(() -> { + final HashSet leftIds = new HashSet<>(ids); + final Collection result = new ArrayList<>(); + ids.stream() + .map(super::getFromCache) + .forEach(q -> { + if (q != null) { + leftIds.remove(q.id); + result.add(q); + } + }); + + if (!leftIds.isEmpty()) { + result.addAll(super.protectedQuizzesRequest(leftIds).getOrThrow()); + } + + return result; + }); + } + + @Override + public Result getQuiz(final String id) { + final QuizData fromCache = super.getFromCache(id); + if (fromCache != null) { + return Result.of(fromCache); + } + + return super.protectedQuizRequest(id); + } + + @Override + protected Supplier> allQuizzesSupplier(final FilterMap filterMap) { + + final String quizName = filterMap.getString(QuizData.FILTER_ATTR_QUIZ_NAME); + 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 () -> { + throw new RuntimeException("TODO"); + }; + } + + @Override + protected Supplier> quizzesSupplier(final Set 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 () -> { + throw new RuntimeException("TODO"); + }; + } + + @Override + protected Supplier 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 () -> { + throw new RuntimeException("TODO"); + }; + } + + @Override + protected Supplier 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 () -> { + throw new RuntimeException("TODO"); + }; + } + + @Override + protected Supplier getCourseChaptersSupplier(final String courseId) { + return () -> { + throw new UnsupportedOperationException("not available yet"); + }; + } + + @Override + public Result getSEBClientRestriction(final Exam exam) { + + 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.ofRuntimeError("TODO"); + } + + @Override + public Result applySEBClientRestriction( + 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.ofRuntimeError("TODO"); + } + + @Override + public Result releaseSEBClientRestriction(final Exam exam) { + + final String quizId = exam.externalId; + + // TODO Release respectively delete all SEB client restrictions for the given + // course / quize on the remote LMS. + + return Result.ofRuntimeError("TODO"); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java new file mode 100644 index 00000000..d64e54e1 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplateFactory.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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.olat; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +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; + +@Lazy +@Service +@WebServiceProfile +public class OlatLmsAPITemplateFactory implements LmsAPITemplateFactory { + + @Override + public LmsType lmsType() { + return LmsType.OPEN_OLAT; + } + + @Override + public Result create(final APITemplateDataSupplier apiTemplateDataSupplier) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java index 834cdfe3..8cd82b35 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/MoodleCourseAccessTest.java @@ -22,6 +22,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import ch.ethz.seb.sebserver.gbl.api.JSONMapper; +import ch.ethz.seb.sebserver.gbl.async.AsyncRunner; import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; @@ -74,7 +75,7 @@ public class MoodleCourseAccessTest { new JSONMapper(), moodleRestTemplateFactory, null, - mock(AsyncService.class), + new AsyncService(new AsyncRunner()), this.env); final String examId = "123";