diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java index 8b1c0805..b3aebfd3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/WebserviceInfo.java @@ -10,9 +10,14 @@ package ch.ethz.seb.sebserver.webservice; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -26,6 +31,8 @@ import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; @WebServiceProfile public class WebserviceInfo { + private static final Logger log = LoggerFactory.getLogger(WebserviceInfo.class); + private static final String WEB_SERVICE_TEST_PROPERTY = "sebserver.test.property"; private static final String WEB_SERVICE_SERVER_NAME_KEY = "sebserver.webservice.http.server.name"; private static final String WEB_SERVICE_HTTP_SCHEME_KEY = "sebserver.webservice.http.scheme"; @@ -33,6 +40,7 @@ public class WebserviceInfo { private static final String WEB_SERVICE_SERVER_PORT_KEY = "server.port"; private static final String WEB_SERVICE_EXAM_API_DISCOVERY_ENDPOINT_KEY = "sebserver.webservice.api.exam.endpoint.discovery"; + private static final String WEB_SERVICE_EXTERNAL_ADDRESS_ALIAS = "sebserver.webservice.lms.address.alias"; private final String testProperty; private final String httpScheme; @@ -42,9 +50,10 @@ public class WebserviceInfo { private final String discoveryEndpoint; private final String serverURLPrefix; - private final boolean isDistributed; + private Map externalAddressAlias; + public WebserviceInfo(final Environment environment) { this.testProperty = environment.getProperty(WEB_SERVICE_TEST_PROPERTY, "NOT_AVAILABLE"); this.httpScheme = environment.getRequiredProperty(WEB_SERVICE_HTTP_SCHEME_KEY); @@ -64,6 +73,25 @@ public class WebserviceInfo { this.isDistributed = BooleanUtils.toBoolean(environment.getProperty( "sebserver.webservice.distributed", Constants.FALSE_STRING)); + + final String addressAlias = environment.getProperty(WEB_SERVICE_EXTERNAL_ADDRESS_ALIAS, ""); + if (StringUtils.isNotBlank(addressAlias)) { + try { + final String[] aliass = StringUtils.split(addressAlias, Constants.LIST_SEPARATOR); + final Map mapping = new LinkedHashMap<>(); + for (final String alias : aliass) { + final String[] nameValue = + StringUtils.split(alias, Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR); + mapping.put(nameValue[0], nameValue[1]); + } + this.externalAddressAlias = Collections.unmodifiableMap(mapping); + } catch (final Exception e) { + log.error("Failed to parse sebserver.webservice.lms.address.alias: ", e); + this.externalAddressAlias = Collections.emptyMap(); + } + } else { + this.externalAddressAlias = Collections.emptyMap(); + } } public String getTestProperty() { @@ -132,6 +160,20 @@ public class WebserviceInfo { return this.isDistributed; } + public Map getExternalAddressAlias() { + return this.externalAddressAlias; + } + + public String getExternalAddressAlias(final String internalAddress) { + return this.externalAddressAlias + .entrySet() + .stream() + .filter(entry -> internalAddress.contains(entry.getKey())) + .findFirst() + .map(entry -> entry.getValue()) + .orElse(null); + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -147,6 +189,8 @@ public class WebserviceInfo { builder.append(this.serverPort); builder.append(", discoveryEndpoint="); builder.append(this.discoveryEndpoint); + builder.append(", externalAddressAlias="); + builder.append(this.externalAddressAlias); builder.append(", serverURLPrefix="); builder.append(this.serverURLPrefix); builder.append(", isDistributed="); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java index 998b61f4..004dc2d8 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPIServiceImpl.java @@ -31,6 +31,7 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; @@ -50,6 +51,7 @@ public class LmsAPIServiceImpl implements LmsAPIService { private final ClientCredentialService clientCredentialService; private final ClientHttpRequestFactory clientHttpRequestFactory; private final String[] openEdxAlternativeTokenRequestPaths; + private final WebserviceInfo webserviceInfo; private final Map cache = new ConcurrentHashMap<>(); @@ -58,12 +60,14 @@ public class LmsAPIServiceImpl implements LmsAPIService { final LmsSetupDAO lmsSetupDAO, final ClientCredentialService clientCredentialService, final ClientHttpRequestFactory clientHttpRequestFactory, + final WebserviceInfo webserviceInfo, @Value("${sebserver.webservice.lms.openedx.api.token.request.paths}") final String alternativeTokenRequestPaths) { this.asyncService = asyncService; this.lmsSetupDAO = lmsSetupDAO; this.clientCredentialService = clientCredentialService; this.clientHttpRequestFactory = clientHttpRequestFactory; + this.webserviceInfo = webserviceInfo; this.openEdxAlternativeTokenRequestPaths = (alternativeTokenRequestPaths != null) ? StringUtils.split(alternativeTokenRequestPaths, Constants.LIST_SEPARATOR) @@ -203,7 +207,8 @@ public class LmsAPIServiceImpl implements LmsAPIService { credentials, this.clientCredentialService, this.clientHttpRequestFactory, - this.openEdxAlternativeTokenRequestPaths); + this.openEdxAlternativeTokenRequestPaths, + this.webserviceInfo); default: throw new UnsupportedOperationException("No support for LMS Type: " + lmsSetup.lmsType); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java index 1be10784..ffaf8e36 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/OpenEdxLmsAPITemplate.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -54,6 +55,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentialService; import ch.ethz.seb.sebserver.webservice.servicelayer.client.ClientCredentials; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; @@ -76,6 +78,7 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { private final ClientHttpRequestFactory clientHttpRequestFactory; private final ClientCredentialService clientCredentialService; private final Set knownTokenAccessPaths; + private final WebserviceInfo webserviceInfo; private OAuth2RestTemplate restTemplate = null; private final MemoizingCircuitBreaker> allQuizzesSupplier; @@ -86,12 +89,14 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { final ClientCredentials credentials, final ClientCredentialService clientCredentialService, final ClientHttpRequestFactory clientHttpRequestFactory, - final String[] alternativeTokenRequestPaths) { + final String[] alternativeTokenRequestPaths, + final WebserviceInfo webserviceInfo) { this.lmsSetup = lmsSetup; this.clientCredentialService = clientCredentialService; this.credentials = credentials; this.clientHttpRequestFactory = clientHttpRequestFactory; + this.webserviceInfo = webserviceInfo; this.knownTokenAccessPaths = new HashSet<>(); this.knownTokenAccessPaths.add(OPEN_EDX_DEFAULT_TOKEN_REQUEST_PATH); if (alternativeTokenRequestPaths != null) { @@ -234,12 +239,13 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { } private ArrayList collectAllQuizzes(final LmsSetup lmsSetup) { + final String externalStartURI = getExternalLMSServerAddress(lmsSetup); return collectAllCourses(lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_ENDPOINT) .stream() .reduce( new ArrayList(), (list, courseData) -> { - list.add(quizDataOf(lmsSetup, courseData)); + list.add(quizDataOf(lmsSetup, courseData, externalStartURI)); return list; }, (list1, list2) -> { @@ -248,6 +254,28 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { }); } + private String getExternalLMSServerAddress(final LmsSetup lmsSetup) { + final String externalAddressAlias = this.webserviceInfo.getExternalAddressAlias(lmsSetup.lmsApiUrl); + String _externalStartURI = lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX; + if (StringUtils.isNoneBlank(externalAddressAlias)) { + try { + + if (log.isDebugEnabled()) { + log.debug("Found external address alias: {}", externalAddressAlias); + } + + final URL url = new URL(lmsSetup.lmsApiUrl); + final int port = url.getPort(); + _externalStartURI = this.webserviceInfo.getHttpScheme() + "://" + externalAddressAlias + ":" + port; + + log.info("Use external address for course access: {}", _externalStartURI); + } catch (final Exception e) { + log.error("Failed to create external address from alias: ", e); + } + } + return _externalStartURI; + } + private List collectAllCourses(final String pageURI) { final List collector = new ArrayList<>(); EdXPage page = getEdxPage(pageURI).getBody(); @@ -275,9 +303,10 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate { private static QuizData quizDataOf( final LmsSetup lmsSetup, - final CourseData courseData) { + final CourseData courseData, + final String uriPrefix) { - final String startURI = lmsSetup.lmsApiUrl + OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX + courseData.id; + final String startURI = uriPrefix + courseData.id; final Map additionalAttrs = new HashMap<>(); additionalAttrs.put("blocks_url", courseData.blocks_url); return new QuizData( diff --git a/src/main/resources/config/application-demo.properties b/src/main/resources/config/application-demo.properties index a4c2aa49..b76e409d 100644 --- a/src/main/resources/config/application-demo.properties +++ b/src/main/resources/config/application-demo.properties @@ -38,6 +38,7 @@ sebserver.webservice.api.exam.accessTokenValiditySeconds=86400 sebserver.webservice.api.pagination.maxPageSize=500 # comma separated list of known possible OpenEdX API access token request endpoints sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token +sebserver.webservice.lms.address.alias=lms.mockup.com=ralph.ethz.ch,edx.devstack.lms=ralph.ethz.ch # write logs to logging.file=log/sebserver.log