code review and cleanup (eclipse code formatting)

This commit is contained in:
anhefti 2021-08-30 10:55:59 +02:00
parent 3c72ed9738
commit 8a72cf1fcd
3 changed files with 166 additions and 172 deletions

View file

@ -8,50 +8,35 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans; package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans;
import java.util.Optional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.Optional;
import java.util.Arrays; import java.util.Set;
import java.util.stream.Stream;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.Locale; import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity; import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.core.env.Environment;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; import org.springframework.web.client.RestTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService; import ch.ethz.seb.sebserver.ClientHttpRequestFactoryService;
import ch.ethz.seb.sebserver.gbl.api.APIMessage; import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.async.AsyncService; import ch.ethz.seb.sebserver.gbl.async.AsyncService;
@ -64,7 +49,6 @@ 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.QuizData;
import ch.ethz.seb.sebserver.gbl.model.exam.SEBRestriction; 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;
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.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult; import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails; import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
@ -75,10 +59,9 @@ 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.LmsAPIService;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCachedCourseAccess;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.AccessibilitySettingsData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.AssignmentData; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.AssignmentData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.UserData; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.UserData;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans.AnsLmsData.AccessibilitySettingsData;
public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate { public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements LmsAPITemplate {
@ -228,42 +211,42 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
private List<QuizData> collectAllQuizzes(final AnsPersonalRestTemplate restTemplate) { private List<QuizData> collectAllQuizzes(final AnsPersonalRestTemplate restTemplate) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final List<QuizData> quizDatas = getAssignments(restTemplate) final List<QuizData> quizDatas = getAssignments(restTemplate)
.stream() .stream()
.map(a -> quizDataFromAnsData(lmsSetup, a)) .map(a -> quizDataFromAnsData(lmsSetup, a))
.collect(Collectors.toList()); .collect(Collectors.toList());
quizDatas.forEach(q -> super.putToCache(q)); quizDatas.forEach(q -> super.putToCache(q));
return quizDatas; return quizDatas;
} }
private QuizData getQuizByAssignmentId(final RestTemplate restTemplate, String id) { private QuizData getQuizByAssignmentId(final RestTemplate restTemplate, final String id) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final AssignmentData a = getAssignmentById(restTemplate, id); final AssignmentData a = getAssignmentById(restTemplate, id);
return quizDataFromAnsData(lmsSetup, a); return quizDataFromAnsData(lmsSetup, a);
} }
private QuizData quizDataFromAnsData(LmsSetup lmsSetup, AssignmentData a) { private QuizData quizDataFromAnsData(final LmsSetup lmsSetup, final AssignmentData a) {
// In Ans, one assignment can have multiple timeslots, but the SEB restriciton // In Ans, one assignment can have multiple timeslots, but the SEB restriciton
// is done at the assignment level, so timeslots don't really matter. // is done at the assignment level, so timeslots don't really matter.
// An assignment's start_at and end_at dates indicate when the first timeslot starts // An assignment's start_at and end_at dates indicate when the first timeslot starts
// and the last timeslot ends. If these are null, there are no timeslots, yet. // and the last timeslot ends. If these are null, there are no timeslots, yet.
// In that case, we create a placeholder timeslot to display in SEB server. // In that case, we create a placeholder timeslot to display in SEB server.
if (a.start_at == null) { if (a.start_at == null) {
a.start_at = java.time.Instant.now().plus(365, java.time.temporal.ChronoUnit.DAYS).toString(); a.start_at = java.time.Instant.now().plus(365, java.time.temporal.ChronoUnit.DAYS).toString();
a.end_at = java.time.Instant.now().plus(366, java.time.temporal.ChronoUnit.DAYS).toString(); a.end_at = java.time.Instant.now().plus(366, java.time.temporal.ChronoUnit.DAYS).toString();
} }
final DateTime startTime = new DateTime(a.start_at); final DateTime startTime = new DateTime(a.start_at);
final DateTime endTime = new DateTime(a.end_at); final DateTime endTime = new DateTime(a.end_at);
return new QuizData( return new QuizData(
String.valueOf(a.id), String.valueOf(a.id),
lmsSetup.getInstitutionId(), lmsSetup.getInstitutionId(),
lmsSetup.id, lmsSetup.id,
lmsSetup.getLmsType(), lmsSetup.getLmsType(),
String.format("%s", a.name), String.format("%s", a.name),
String.format(""), String.format(""),
startTime, startTime,
endTime, endTime,
a.start_url, a.start_url,
Map.of("assignment_id", String.valueOf(a.id))); Map.of("assignment_id", String.valueOf(a.id)));
} }
private List<AssignmentData> getAssignments(final RestTemplate restTemplate) { private List<AssignmentData> getAssignments(final RestTemplate restTemplate) {
@ -272,15 +255,16 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
// we can only list those for which seb server has been enabled in Ans (like in OLAT): // we can only list those for which seb server has been enabled in Ans (like in OLAT):
//final String url = "/api/v2/search/assignments?query=seb_server_enabled:true"; //final String url = "/api/v2/search/assignments?query=seb_server_enabled:true";
final String url = "/api/v2/search/assignments"; final String url = "/api/v2/search/assignments";
return this.apiGetList(restTemplate, url, new ParameterizedTypeReference<List<AssignmentData>>(){}); return this.apiGetList(restTemplate, url, new ParameterizedTypeReference<List<AssignmentData>>() {
});
} }
private AssignmentData getAssignmentById(final RestTemplate restTemplate, String id) { private AssignmentData getAssignmentById(final RestTemplate restTemplate, final String id) {
final String url = String.format("/api/v2/assignments/%s", id); final String url = String.format("/api/v2/assignments/%s", id);
return this.apiGet(restTemplate, url, AssignmentData.class); return this.apiGet(restTemplate, url, AssignmentData.class);
} }
private List<QuizData> getQuizzesByIds(final RestTemplate restTemplate, final Set<String> ids) { private List<QuizData> getQuizzesByIds(final RestTemplate restTemplate, final Set<String> ids) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
return ids.stream().map(id -> { return ids.stream().map(id -> {
final String url = String.format("/api/v2/assignments/%s", id); final String url = String.format("/api/v2/assignments/%s", id);
@ -290,7 +274,7 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
super.putToCache(quizData); super.putToCache(quizData);
return quizData; return quizData;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override @Override
@ -300,11 +284,11 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
// on the API level and always retrieve all assignments and let SEB server // on the API level and always retrieve all assignments and let SEB server
// do the filtering. // do the filtering.
return () -> { return () -> {
List<QuizData> res = getRestTemplate() final List<QuizData> res = getRestTemplate()
.map(this::collectAllQuizzes) .map(this::collectAllQuizzes)
.getOrThrow(); .getOrThrow();
super.putToCache(res); super.putToCache(res);
return res; return res;
}; };
} }
@ -323,19 +307,18 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
} }
private ExamineeAccountDetails getExamineeById(final RestTemplate restTemplate, final String id) { private ExamineeAccountDetails getExamineeById(final RestTemplate restTemplate, final String id) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final String url = String.format("/api/v2/users/%s", id); final String url = String.format("/api/v2/users/%s", id);
UserData u = this.apiGet(restTemplate, url, UserData.class); final UserData u = this.apiGet(restTemplate, url, UserData.class);
final Map<String, String> attrs = new HashMap<>(); final Map<String, String> attrs = new HashMap<>();
attrs.put("role", u.role); attrs.put("role", u.role);
attrs.put("affiliation", u.affiliation); attrs.put("affiliation", u.affiliation);
attrs.put("active", u.active ? "yes": "no"); attrs.put("active", u.active ? "yes" : "no");
return new ExamineeAccountDetails( return new ExamineeAccountDetails(
String.valueOf(u.id), String.valueOf(u.id),
u.last_name + ", " + u.first_name, u.last_name + ", " + u.first_name,
u.external_id, u.external_id,
u.email_address, u.email_address,
attrs); attrs);
} }
@Override @Override
@ -358,29 +341,34 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
.map(t -> this.getRestrictionForAssignmentId(t, exam.externalId)); .map(t -> this.getRestrictionForAssignmentId(t, exam.externalId));
} }
private SEBRestriction getRestrictionForAssignmentId(final RestTemplate restTemplate, String id) { private SEBRestriction getRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
final String url = String.format("/api/v2/assignments/%s", id); final String url = String.format("/api/v2/assignments/%s", id);
final AssignmentData assignment = this.apiGet(restTemplate, url, AssignmentData.class); final AssignmentData assignment = this.apiGet(restTemplate, url, AssignmentData.class);
final AccessibilitySettingsData ts = assignment.accessibility_settings; final AccessibilitySettingsData ts = assignment.accessibility_settings;
return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>()); return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>());
} }
private SEBRestriction setRestrictionForAssignmentId(final RestTemplate restTemplate, String id, SEBRestriction restriction) { private SEBRestriction setRestrictionForAssignmentId(final RestTemplate restTemplate, final String id,
final SEBRestriction restriction) {
final String url = String.format("/api/v2/assignments/%s", id); final String url = String.format("/api/v2/assignments/%s", id);
final AssignmentData assignment = getAssignmentById(restTemplate, id); final AssignmentData assignment = getAssignmentById(restTemplate, id);
assignment.accessibility_settings.config_keys = new ArrayList<>(restriction.configKeys); assignment.accessibility_settings.config_keys = new ArrayList<>(restriction.configKeys);
assignment.accessibility_settings.seb_server_enabled = true; assignment.accessibility_settings.seb_server_enabled = true;
final AssignmentData r = this.apiPatch(restTemplate, url, assignment, AssignmentData.class, AssignmentData.class); @SuppressWarnings("unused")
final AssignmentData r =
this.apiPatch(restTemplate, url, assignment, AssignmentData.class, AssignmentData.class);
final AccessibilitySettingsData ts = assignment.accessibility_settings; final AccessibilitySettingsData ts = assignment.accessibility_settings;
return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>()); return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>());
} }
private SEBRestriction deleteRestrictionForAssignmentId(final RestTemplate restTemplate, String id) { private SEBRestriction deleteRestrictionForAssignmentId(final RestTemplate restTemplate, final String id) {
final String url = String.format("/api/v2/assignments/%s", id); final String url = String.format("/api/v2/assignments/%s", id);
final AssignmentData assignment = getAssignmentById(restTemplate, id); final AssignmentData assignment = getAssignmentById(restTemplate, id);
assignment.accessibility_settings.config_keys = null; assignment.accessibility_settings.config_keys = null;
assignment.accessibility_settings.seb_server_enabled = false; assignment.accessibility_settings.seb_server_enabled = false;
final AssignmentData r = this.apiPatch(restTemplate, url, assignment, AssignmentData.class, AssignmentData.class); @SuppressWarnings("unused")
final AssignmentData r =
this.apiPatch(restTemplate, url, assignment, AssignmentData.class, AssignmentData.class);
final AccessibilitySettingsData ts = assignment.accessibility_settings; final AccessibilitySettingsData ts = assignment.accessibility_settings;
return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>()); return new SEBRestriction(Long.valueOf(id), ts.config_keys, null, new HashMap<String, String>());
} }
@ -395,50 +383,58 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
@Override @Override
public Result<Exam> releaseSEBClientRestriction(final Exam exam) { public Result<Exam> releaseSEBClientRestriction(final Exam exam) {
final String quizId = exam.externalId;
return getRestTemplate() return getRestTemplate()
.map(t -> this.deleteRestrictionForAssignmentId(t, exam.externalId)) .map(t -> this.deleteRestrictionForAssignmentId(t, exam.externalId))
.map(x -> exam); .map(x -> exam);
} }
private enum LinkRel { private enum LinkRel {
FIRST, LAST, PREV, NEXT FIRST, LAST, PREV, NEXT
} }
private class PageLink { private class PageLink {
public String link; public final String link;
public LinkRel rel; public final LinkRel rel;
public PageLink(String l, LinkRel r) { link = l; rel = r; }
public PageLink(final String l, final LinkRel r) {
this.link = l;
this.rel = r;
}
} }
private List<PageLink> parseLinks(String header) {
private List<PageLink> parseLinks(final String header) {
// Extracts the individual links from a header that looks like this: // Extracts the individual links from a header that looks like this:
// <https://staging.ans.app/api/v2/search/assignments?query=seb_server_enabled%3Atrue&page=1&items=20>; rel="first",<https://staging.ans.app/api/v2/search/assignments?query=seb_server_enabled%3Atrue&page=1&items=20>; rel="last" // <https://staging.ans.app/api/v2/search/assignments?query=seb_server_enabled%3Atrue&page=1&items=20>; rel="first",<https://staging.ans.app/api/v2/search/assignments?query=seb_server_enabled%3Atrue&page=1&items=20>; rel="last"
final Stream<String> links = Arrays.stream(header.split(",")); final Stream<String> links = Arrays.stream(header.split(","));
return links return links
.map(s -> { .map(s -> {
String[] pair = s.split(";"); final String[] pair = s.split(";");
String link = pair[0].trim().substring(1).replaceFirst(".$",""); // remove < > final String link = pair[0].trim().substring(1).replaceFirst(".$", ""); // remove < >
String relName = pair[1].trim().substring(5).replaceFirst(".$",""); // remove rel=" " final String relName = pair[1].trim().substring(5).replaceFirst(".$", ""); // remove rel=" "
return new PageLink(link, LinkRel.valueOf(relName.toUpperCase(Locale.ROOT))); return new PageLink(link, LinkRel.valueOf(relName.toUpperCase(Locale.ROOT)));
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private Optional<PageLink> getNextLink(List<PageLink> links) {
private Optional<PageLink> getNextLink(final List<PageLink> links) {
return links.stream().filter(l -> l.rel == LinkRel.NEXT).findFirst(); return links.stream().filter(l -> l.rel == LinkRel.NEXT).findFirst();
} }
private <T> List<T> apiGetList(final RestTemplate restTemplate, String url, ParameterizedTypeReference<List<T>> type) { private <T> List<T> apiGetList(final RestTemplate restTemplate, final String url,
final ParameterizedTypeReference<List<T>> type) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
return apiGetListPages(restTemplate, lmsSetup.lmsApiUrl + url, type); return apiGetListPages(restTemplate, lmsSetup.lmsApiUrl + url, type);
} }
private <T> List<T> apiGetListPages(final RestTemplate restTemplate, String link, ParameterizedTypeReference<List<T>> type) { private <T> List<T> apiGetListPages(final RestTemplate restTemplate, final String link,
final ParameterizedTypeReference<List<T>> type) {
// unlike the other api methods, this one takes an explicit link // unlike the other api methods, this one takes an explicit link
// instead of prepending lmsSetup.lmsApiUrl. This is done because Ans // instead of prepending lmsSetup.lmsApiUrl. This is done because Ans
// provides absolute links for pagination. This method calls itself // provides absolute links for pagination. This method calls itself
// recursively to retrieve multiple pages. // recursively to retrieve multiple pages.
final HttpHeaders requestHeaders = new HttpHeaders(); final HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("accept", "application/json"); requestHeaders.set("accept", "application/json");
ResponseEntity<List<T>> response = restTemplate.exchange( final ResponseEntity<List<T>> response = restTemplate.exchange(
link, link,
HttpMethod.GET, HttpMethod.GET,
new HttpEntity<>(requestHeaders), new HttpEntity<>(requestHeaders),
@ -446,18 +442,18 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
final List<T> page = response.getBody(); final List<T> page = response.getBody();
final HttpHeaders responseHeaders = response.getHeaders(); final HttpHeaders responseHeaders = response.getHeaders();
final List<PageLink> links = parseLinks(responseHeaders.getFirst("link")); final List<PageLink> links = parseLinks(responseHeaders.getFirst("link"));
final List<T> nextPage = getNextLink(links).map( l -> { final List<T> nextPage = getNextLink(links).map(l -> {
return apiGetListPages(restTemplate, l.link, type); return apiGetListPages(restTemplate, l.link, type);
}).orElse(new ArrayList<T>()); }).orElse(new ArrayList<T>());
page.addAll(nextPage); page.addAll(nextPage);
return page; return page;
} }
private <T> T apiGet(final RestTemplate restTemplate, String url, Class<T> type) { private <T> T apiGet(final RestTemplate restTemplate, final String url, final Class<T> type) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final HttpHeaders requestHeaders = new HttpHeaders(); final HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("accept", "application/json"); requestHeaders.set("accept", "application/json");
ResponseEntity<T> res = restTemplate.exchange( final ResponseEntity<T> res = restTemplate.exchange(
lmsSetup.lmsApiUrl + url, lmsSetup.lmsApiUrl + url,
HttpMethod.GET, HttpMethod.GET,
new HttpEntity<>(requestHeaders), new HttpEntity<>(requestHeaders),
@ -465,11 +461,12 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
return res.getBody(); return res.getBody();
} }
private <P,R> R apiPatch(final RestTemplate restTemplate, String url, P patch, Class<P> patchType, Class<R> responseType) { private <P, R> R apiPatch(final RestTemplate restTemplate, final String url, final P patch,
final Class<P> patchType, final Class<R> responseType) {
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final HttpHeaders requestHeaders = new HttpHeaders(); final HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("content-type", "application/json"); requestHeaders.set("content-type", "application/json");
HttpEntity<P> requestEntity = new HttpEntity<>(patch, requestHeaders); final HttpEntity<P> requestEntity = new HttpEntity<>(patch, requestHeaders);
final ResponseEntity<R> res = restTemplate.exchange( final ResponseEntity<R> res = restTemplate.exchange(
lmsSetup.lmsApiUrl + url, lmsSetup.lmsApiUrl + url,
HttpMethod.PATCH, HttpMethod.PATCH,
@ -478,16 +475,14 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
return res.getBody(); return res.getBody();
} }
private Result<AnsPersonalRestTemplate> getRestTemplate() { private Result<AnsPersonalRestTemplate> getRestTemplate() {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
if (this.cachedRestTemplate != null) { return this.cachedRestTemplate; } if (this.cachedRestTemplate != null) {
return this.cachedRestTemplate;
}
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials(); final ClientCredentials credentials = this.apiTemplateDataSupplier.getLmsClientCredentials();
final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData(); final ProxyData proxyData = this.apiTemplateDataSupplier.getProxyData();
final CharSequence plainClientId = credentials.clientId;
final CharSequence plainClientSecret = this.clientCredentialService final CharSequence plainClientSecret = this.clientCredentialService
.getPlainClientSecret(credentials) .getPlainClientSecret(credentials)
.getOrThrow(); .getOrThrow();

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.ans;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
public final class AnsLmsData { public final class AnsLmsData {
@ -24,44 +23,45 @@ public final class AnsLmsData {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
static final class AssignmentData { static final class AssignmentData {
/* Ans API example: /*
{ * Ans API example:
"id": 78711, * {
"course_id": 44412, * "id": 78711,
"name": "Digital test demo", * "course_id": 44412,
"summative": false, * "name": "Digital test demo",
"assignment_type": "Quiz", * "summative": false,
"start_at": "2021-08-18T09:00:00.000+02:00", * "assignment_type": "Quiz",
"end_at": "2021-08-18T12:00:00.000+02:00", * "start_at": "2021-08-18T09:00:00.000+02:00",
"created_at": "2021-06-21T12:24:28.538+02:00", * "end_at": "2021-08-18T12:00:00.000+02:00",
"updated_at": "2021-08-17T03:41:56.747+02:00", * "created_at": "2021-06-21T12:24:28.538+02:00",
"trashed": false, * "updated_at": "2021-08-17T03:41:56.747+02:00",
"start_url": "https://staging.ans.app/digital_test/assignments/78805/results/new", * "trashed": false,
"accessibility_settings": { * "start_url": "https://staging.ans.app/digital_test/assignments/78805/results/new",
"attempts": 1, * "accessibility_settings": {
"restricted_access_to_other_pages": false, * "attempts": 1,
"notes": false, * "restricted_access_to_other_pages": false,
"spellchecker": false, * "notes": false,
"feedback": false, * "spellchecker": false,
"forced_test_navigation": false, * "feedback": false,
"cannot_reopen_question_groups": false, * "forced_test_navigation": false,
"seb_server_enabled": true, * "cannot_reopen_question_groups": false,
"config_keys": [ * "seb_server_enabled": true,
"9dd14ac828617116a1230c71b9a1aa9e06f43b32d9fa7db67f4fa113a6896e83e" * "config_keys": [
] * "9dd14ac828617116a1230c71b9a1aa9e06f43b32d9fa7db67f4fa113a6896e83e"
}, * ]
"grades_settings": { * },
"grade_calculation": "formula", * "grades_settings": {
"grade_formula": "1 + 9 * points / total", * "grade_calculation": "formula",
"rounding": "decimal", * "grade_formula": "1 + 9 * points / total",
"grade_lower_bound": true, * "rounding": "decimal",
"grade_lower_limit": "1.0", * "grade_lower_bound": true,
"grade_upper_bound": true, * "grade_lower_limit": "1.0",
"grade_upper_limit": "10.0", * "grade_upper_bound": true,
"guess_correction": false, * "grade_upper_limit": "10.0",
"passed_grade": "5.5" * "guess_correction": false,
} * "passed_grade": "5.5"
} * }
* }
*/ */
public long id; public long id;
public long course_id; public long course_id;
@ -75,21 +75,22 @@ public final class AnsLmsData {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
static final class UserData { static final class UserData {
/* Ans API example: /*
{ * Ans API example:
"id": 726404, * {
"student_number": null, * "id": 726404,
"first_name": "John", * "student_number": null,
"middle_name": null, * "first_name": "John",
"last_name": "Doe", * "middle_name": null,
"external_id": null, * "last_name": "Doe",
"created_at": "2021-06-21T12:07:11.668+02:00", * "external_id": null,
"updated_at": "2021-07-26T20:16:01.638+02:00", * "created_at": "2021-06-21T12:07:11.668+02:00",
"active": true, * "updated_at": "2021-07-26T20:16:01.638+02:00",
"email_address": "person@example.org", * "active": true,
"affiliation": "employee", * "email_address": "person@example.org",
"role": "owner" * "affiliation": "employee",
} * "role": "owner"
* }
*/ */
public long id; public long id;
public String first_name; public String first_name;

View file

@ -12,33 +12,31 @@ import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
import org.springframework.web.client.RestTemplate;
public class AnsPersonalRestTemplate extends RestTemplate { public class AnsPersonalRestTemplate extends RestTemplate {
private static final Logger log = LoggerFactory.getLogger(AnsPersonalRestTemplate.class); private static final Logger log = LoggerFactory.getLogger(AnsPersonalRestTemplate.class);
public String token; public String token;
public AnsPersonalRestTemplate(ClientCredentialsResourceDetails details) {
super();
token = details.getClientSecret();
this.getInterceptors().add(new ClientHttpRequestInterceptor(){
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set("Authorization", "Bearer " + token);
//log.debug("Matching curl: curl -X GET {} -H 'accept: application/json' -H 'Authorization: Bearer {}'", request.getURI(), token);
ClientHttpResponse response = execution.execute(request, body);
log.debug("Response Headers : {}", response.getHeaders());
return response;
}
});
}
}
public AnsPersonalRestTemplate(final ClientCredentialsResourceDetails details) {
super();
this.token = details.getClientSecret();
this.getInterceptors().add(new ClientHttpRequestInterceptor() {
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().set("Authorization", "Bearer " + AnsPersonalRestTemplate.this.token);
//log.debug("Matching curl: curl -X GET {} -H 'accept: application/json' -H 'Authorization: Bearer {}'", request.getURI(), token);
final ClientHttpResponse response = execution.execute(request, body);
log.debug("Response Headers : {}", response.getHeaders());
return response;
}
});
}
}