minor improvements, re-use OlatLmsRestTemplate with existing token
This commit is contained in:
parent
ae5149226a
commit
59da4bcf4e
3 changed files with 80 additions and 73 deletions
|
@ -75,6 +75,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
private final APITemplateDataSupplier apiTemplateDataSupplier;
|
private final APITemplateDataSupplier apiTemplateDataSupplier;
|
||||||
private final Long lmsSetupId;
|
private final Long lmsSetupId;
|
||||||
|
|
||||||
|
private OlatLmsRestTemplate cachedRestTemplate;
|
||||||
|
|
||||||
protected OlatLmsAPITemplate(
|
protected OlatLmsAPITemplate(
|
||||||
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
final ClientHttpRequestFactoryService clientHttpRequestFactoryService,
|
||||||
final ClientCredentialService clientCredentialService,
|
final ClientCredentialService clientCredentialService,
|
||||||
|
@ -112,9 +114,8 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
if (testLmsSetupSettings.hasAnyError()) {
|
if (testLmsSetupSettings.hasAnyError()) {
|
||||||
return testLmsSetupSettings;
|
return testLmsSetupSettings;
|
||||||
}
|
}
|
||||||
// TODO: improve error handling
|
|
||||||
try {
|
try {
|
||||||
final RestTemplate restTemplate = this.getRestTemplate().get();
|
this.getRestTemplate().get();
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.OPEN_OLAT, "Unspecific error connecting to OLAT API");
|
return LmsSetupTestResult.ofQuizAccessAPIError(LmsType.OPEN_OLAT, "Unspecific error connecting to OLAT API");
|
||||||
|
@ -124,20 +125,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LmsSetupTestResult testCourseRestrictionAPI() {
|
public LmsSetupTestResult testCourseRestrictionAPI() {
|
||||||
// TODO: Any reason to implement a separate check or is this good enough?
|
|
||||||
return testCourseAccessAPI();
|
return testCourseAccessAPI();
|
||||||
|
|
||||||
/*final LmsSetupTestResult testLmsSetupSettings = testLmsSetupSettings();
|
|
||||||
if (testLmsSetupSettings.hasAnyError()) {
|
|
||||||
return testLmsSetupSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LmsType.OPEN_OLAT.features.contains(Features.SEB_RESTRICTION)) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return LmsSetupTestResult.ofOkay(LmsType.OPEN_OLAT);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private LmsSetupTestResult testLmsSetupSettings() {
|
private LmsSetupTestResult testLmsSetupSettings() {
|
||||||
|
@ -233,10 +221,9 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String examUrl(long olatTestId) {
|
private String examUrl(long olatRepositoryId) {
|
||||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||||
// TODO: at the moment, we don't know olatTestId because we get the assessment mode id (a.key), not the test id.
|
return lmsSetup.lmsApiUrl + "/auth/RepositoryEntry/" + olatRepositoryId;
|
||||||
return lmsSetup.lmsApiUrl + "/auth/RepositoryEntry/" + olatTestId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<QuizData> collectAllQuizzes(final OlatLmsRestTemplate restTemplate, final FilterMap filterMap) {
|
private List<QuizData> collectAllQuizzes(final OlatLmsRestTemplate restTemplate, final FilterMap filterMap) {
|
||||||
|
@ -251,8 +238,9 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
|
|
||||||
final List<AssessmentData> as = this.apiGetList(restTemplate, url, new ParameterizedTypeReference<List<AssessmentData>>(){});
|
final List<AssessmentData> as = this.apiGetList(restTemplate, url, new ParameterizedTypeReference<List<AssessmentData>>(){});
|
||||||
return as.stream()
|
return as.stream()
|
||||||
.map(a -> new QuizData(
|
.map(a -> {
|
||||||
String.format("%d", a.assessmentModeKey),
|
return new QuizData(
|
||||||
|
String.format("%d", a.key),
|
||||||
lmsSetup.getInstitutionId(),
|
lmsSetup.getInstitutionId(),
|
||||||
lmsSetup.id,
|
lmsSetup.id,
|
||||||
lmsSetup.getLmsType(),
|
lmsSetup.getLmsType(),
|
||||||
|
@ -261,7 +249,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
Utils.toDateTimeUTC(a.dateFrom),
|
Utils.toDateTimeUTC(a.dateFrom),
|
||||||
Utils.toDateTimeUTC(a.dateTo),
|
Utils.toDateTimeUTC(a.dateTo),
|
||||||
examUrl(a.repositoryEntryKey),
|
examUrl(a.repositoryEntryKey),
|
||||||
new HashMap<String, String>()))
|
new HashMap<String, String>());})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +271,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
final String url = String.format("/restapi/assessment_modes/%s", id);
|
final String url = String.format("/restapi/assessment_modes/%s", id);
|
||||||
final AssessmentData a = this.apiGet(restTemplate, url, AssessmentData.class);
|
final AssessmentData a = this.apiGet(restTemplate, url, AssessmentData.class);
|
||||||
return new QuizData(
|
return new QuizData(
|
||||||
String.format("%d", a.assessmentModeKey),
|
String.format("%d", a.key),
|
||||||
lmsSetup.getInstitutionId(),
|
lmsSetup.getInstitutionId(),
|
||||||
lmsSetup.id,
|
lmsSetup.id,
|
||||||
lmsSetup.getLmsType(),
|
lmsSetup.getLmsType(),
|
||||||
|
@ -303,8 +291,7 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
String.valueOf(u.key),
|
String.valueOf(u.key),
|
||||||
u.lastName + ", " + u.firstName,
|
u.lastName + ", " + u.firstName,
|
||||||
u.username,
|
u.username,
|
||||||
// TODO: other placeholder value? null?
|
"OLAT API does not provide email addresses",
|
||||||
"OLAT does not provide email addresses",
|
|
||||||
attrs);
|
attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,18 +335,16 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<SEBRestriction> getSEBClientRestriction(final Exam exam) {
|
public Result<SEBRestriction> getSEBClientRestriction(final Exam exam) {
|
||||||
return Result.of(getRestTemplate()
|
return getRestTemplate()
|
||||||
.map(t -> this.getRestrictionForAssignmentId(t, exam.externalId))
|
.map(t -> this.getRestrictionForAssignmentId(t, exam.externalId));
|
||||||
.getOrThrow());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<SEBRestriction> applySEBClientRestriction(
|
public Result<SEBRestriction> applySEBClientRestriction(
|
||||||
final String externalExamId,
|
final String externalExamId,
|
||||||
final SEBRestriction sebRestrictionData) {
|
final SEBRestriction sebRestrictionData) {
|
||||||
return Result.of(getRestTemplate()
|
return getRestTemplate()
|
||||||
.map(t -> this.setRestrictionForAssignmentId(t, externalExamId, sebRestrictionData))
|
.map(t -> this.setRestrictionForAssignmentId(t, externalExamId, sebRestrictionData));
|
||||||
.getOrThrow());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -367,10 +352,9 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final String quizId = exam.externalId;
|
final String quizId = exam.externalId;
|
||||||
|
|
||||||
return Result.of(getRestTemplate()
|
return getRestTemplate()
|
||||||
.map(t -> this.deleteRestrictionForAssignmentId(t, exam.externalId))
|
.map(t -> this.deleteRestrictionForAssignmentId(t, exam.externalId))
|
||||||
.map(x -> exam)
|
.map(x -> exam);
|
||||||
.getOrThrow());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T apiGet(final RestTemplate restTemplate, String url, Class<T> type) {
|
private <T> T apiGet(final RestTemplate restTemplate, String url, Class<T> type) {
|
||||||
|
@ -417,29 +401,33 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<OlatLmsRestTemplate> getRestTemplate() {
|
private Result<OlatLmsRestTemplate> getRestTemplate() {
|
||||||
// TODO: cache/reuse authenticated template for more than 1 request?
|
return Result.tryCatch(() -> {
|
||||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
if (this.cachedRestTemplate != null) { return this.cachedRestTemplate; }
|
||||||
final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials();
|
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||||
final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData();
|
final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials();
|
||||||
|
final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData();
|
||||||
|
|
||||||
final CharSequence plainClientId = credentials.clientId;
|
final CharSequence plainClientId = credentials.clientId;
|
||||||
final CharSequence plainClientSecret = this.clientCredentialService
|
final CharSequence plainClientSecret = this.clientCredentialService
|
||||||
.getPlainClientSecret(credentials)
|
.getPlainClientSecret(credentials)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
final ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
|
||||||
details.setAccessTokenUri(lmsSetup.lmsApiUrl + "/restapi/auth/");
|
details.setAccessTokenUri(lmsSetup.lmsApiUrl + "/restapi/auth/");
|
||||||
details.setClientId(plainClientId.toString());
|
details.setClientId(plainClientId.toString());
|
||||||
details.setClientSecret(plainClientSecret.toString());
|
details.setClientSecret(plainClientSecret.toString());
|
||||||
|
|
||||||
final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService
|
final ClientHttpRequestFactory clientHttpRequestFactory = this.clientHttpRequestFactoryService
|
||||||
.getClientHttpRequestFactory(proxyData)
|
.getClientHttpRequestFactory(proxyData)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final OlatLmsRestTemplate template = new OlatLmsRestTemplate(details);
|
final OlatLmsRestTemplate template = new OlatLmsRestTemplate(details);
|
||||||
template.setRequestFactory(clientHttpRequestFactory);
|
template.setRequestFactory(clientHttpRequestFactory);
|
||||||
|
|
||||||
return Result.of(template);
|
this.cachedRestTemplate = template;
|
||||||
|
|
||||||
|
return this.cachedRestTemplate;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ public final class OlatLmsData {
|
||||||
"dateFrom": 1624420800000,
|
"dateFrom": 1624420800000,
|
||||||
"dateTo": 1624658400000,
|
"dateTo": 1624658400000,
|
||||||
"description": "",
|
"description": "",
|
||||||
"assessmentModeKey": 6356992,
|
"key": 6356992,
|
||||||
“repositoryEntryKey”: 462324,
|
“repositoryEntryKey”: 462324,
|
||||||
"name": "SEB test"
|
"name": "SEB test"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
public long assessmentModeKey;
|
public long key;
|
||||||
public long repositoryEntryKey;
|
public long repositoryEntryKey;
|
||||||
public String name;
|
public String name;
|
||||||
public String description;
|
public String description;
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.HttpRequest;
|
import org.springframework.http.HttpRequest;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -25,35 +26,53 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
|
||||||
public class OlatLmsRestTemplate extends RestTemplate {
|
public class OlatLmsRestTemplate extends RestTemplate {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(OlatLmsRestTemplate.class);
|
private static final Logger log = LoggerFactory.getLogger(OlatLmsRestTemplate.class);
|
||||||
|
|
||||||
public String token;
|
public String token;
|
||||||
|
private ClientCredentialsResourceDetails details;
|
||||||
|
|
||||||
public OlatLmsRestTemplate(ClientCredentialsResourceDetails details) {
|
public OlatLmsRestTemplate(ClientCredentialsResourceDetails details) {
|
||||||
super();
|
super();
|
||||||
|
this.details = details;
|
||||||
// Authenticate with OLAT and store the received X-OLAT-TOKEN
|
authenticate();
|
||||||
final String authUrl = String.format("%s%s?password=%s",
|
|
||||||
details.getAccessTokenUri(),
|
|
||||||
details.getClientId(),
|
|
||||||
details.getClientSecret());
|
|
||||||
final HttpHeaders httpHeaders = new HttpHeaders();
|
|
||||||
httpHeaders.set("accept", "application/json");
|
|
||||||
ResponseEntity<String> response = this.getForEntity(authUrl, String.class);
|
|
||||||
HttpHeaders responseHeaders = response.getHeaders();
|
|
||||||
log.debug("OLAT Auth Response Headers: {}", responseHeaders);
|
|
||||||
token = responseHeaders.getFirst("X-OLAT-TOKEN");
|
|
||||||
|
|
||||||
// Add X-OLAT-TOKEN request header to every request done using this RestTemplate
|
// Add X-OLAT-TOKEN request header to every request done using this RestTemplate
|
||||||
this.getInterceptors().add(new ClientHttpRequestInterceptor(){
|
this.getInterceptors().add(new ClientHttpRequestInterceptor(){
|
||||||
@Override
|
@Override
|
||||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||||
request.getHeaders().set("X-OLAT-TOKEN", token);
|
request.getHeaders().set("accept", "application/json");
|
||||||
request.getHeaders().set("accept", "application/json");
|
// if we don't have a token (this is normal during authentication), just do the call
|
||||||
HttpHeaders responseHeaders = response.getHeaders();
|
if (token == null) { return execution.execute(request, body); }
|
||||||
return execution.execute(request, body);
|
// otherwise, add the X-OLAT-TOKEN
|
||||||
|
request.getHeaders().set("X-OLAT-TOKEN", token);
|
||||||
|
ClientHttpResponse response = execution.execute(request, body);
|
||||||
|
log.debug("OLAT [regular API call] Response Headers: {}", response.getHeaders());
|
||||||
|
// If we get a 401, re-authenticate and try once more
|
||||||
|
if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {
|
||||||
|
authenticate();
|
||||||
|
request.getHeaders().set("X-OLAT-TOKEN", token);
|
||||||
|
response = execution.execute(request, body);
|
||||||
|
log.debug("OLAT [retry API call] Response Headers: {}", response.getHeaders());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void authenticate() {
|
||||||
|
// Authenticate with OLAT and store the received X-OLAT-TOKEN
|
||||||
|
token = null;
|
||||||
|
final String authUrl = String.format("%s%s?password=%s",
|
||||||
|
details.getAccessTokenUri(),
|
||||||
|
details.getClientId(),
|
||||||
|
details.getClientSecret());
|
||||||
|
final HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
ResponseEntity<String> response = this.getForEntity(authUrl, String.class);
|
||||||
|
HttpHeaders responseHeaders = response.getHeaders();
|
||||||
|
log.debug("OLAT [authenticate] Response Headers: {}", responseHeaders);
|
||||||
|
token = responseHeaders.getFirst("X-OLAT-TOKEN");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue