code review and cleanup (eclipse code formatting)
This commit is contained in:
parent
3c72ed9738
commit
8a72cf1fcd
3 changed files with 166 additions and 172 deletions
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue