SEBSERV-418 removed SEB config upload to Moodle and call set_exam_data on change instead

This commit is contained in:
anhefti 2024-06-26 10:57:18 +02:00
parent d291e21543
commit 01bd5c5558
8 changed files with 75 additions and 144 deletions

View file

@ -31,7 +31,7 @@ public interface FullLmsIntegrationAPI {
Result<ExamData> applyExamData(ExamData examData); Result<ExamData> applyExamData(ExamData examData);
Result<Exam> applyConnectionConfiguration(Exam exam, byte[] configData); //Result<Exam> applyConnectionConfiguration(Exam exam, byte[] configData);
Result<String> deleteConnectionDetails(); Result<String> deleteConnectionDetails();

View file

@ -8,10 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl; package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -53,7 +50,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConnectionConfigu
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConnectionConfigurationService; import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConnectionConfigurationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -152,11 +148,9 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
if (hasFullIntegration(exam.lmsSetupId)) { if (hasFullIntegration(exam.lmsSetupId)) {
this.applyExamData(exam, !exam.active); this.applyExamData(exam, !exam.active);
this.applyConnectionConfiguration(exam);
} }
return exam; return exam;
}); });
} }
@Override @Override
@ -245,7 +239,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.getOr(Collections.emptyList()) .getOr(Collections.emptyList())
.stream() .stream()
.filter(exam -> this.needsConnectionConfigurationChange(exam, event.configId)) .filter(exam -> this.needsConnectionConfigurationChange(exam, event.configId))
.forEach(this::applyConnectionConfiguration); .forEach(exam -> applyExamData(exam, false));
} }
@Override @Override
@ -332,8 +326,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.map(template -> getQuizData(template, courseId, quizId, examData)) .map(template -> getQuizData(template, courseId, quizId, examData))
.map(createExam(examTemplateId, showQuitLink, quitPassword)) .map(createExam(examTemplateId, showQuitLink, quitPassword))
.map(exam -> applyExamData(exam, false)) .map(exam -> applyExamData(exam, false))
.map(this::applySEBClientRestrictionIfRunning) .map(this::applySEBClientRestrictionIfRunning);
.map(this::applyConnectionConfiguration);
} }
private Exam applySEBClientRestrictionIfRunning(final Exam exam) { private Exam applySEBClientRestrictionIfRunning(final Exam exam) {
@ -502,7 +495,6 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.byModelId(examTemplateId) .byModelId(examTemplateId)
.getOrThrow(); .getOrThrow();
// import exam // import exam
final POSTMapper post = new POSTMapper(null, null); final POSTMapper post = new POSTMapper(null, null);
post.putIfAbsent(Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, examTemplateId); post.putIfAbsent(Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, examTemplateId);
@ -526,12 +518,11 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
} }
private Exam checkDeletion(final Exam exam) { private Exam checkDeletion(final Exam exam) {
// TODO check if Exam can be deleted according to the Spec
if (exam.status != Exam.ExamStatus.RUNNING) { if (exam.status != Exam.ExamStatus.RUNNING) {
return exam; return exam;
} }
// if exam is running and has active SEB client connections, it cannot be deleted
final Integer active = this.clientConnectionDAO final Integer active = this.clientConnectionDAO
.getAllActiveConnectionTokens(exam.id) .getAllActiveConnectionTokens(exam.id)
.map(Collection::size) .map(Collection::size)
@ -545,15 +536,6 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
throw new APIMessage.APIMessageException( throw new APIMessage.APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION APIMessage.ErrorMessage.INTEGRITY_VALIDATION
.of("Exam currently has active SEB Client connections.")); .of("Exam currently has active SEB Client connections."));
// check if there are no active SEB client connections
// if (this.examSessionService.hasActiveSEBClientConnections(exam.id)) {
// throw new APIMessage.APIMessageException(
// APIMessage.ErrorMessage.INTEGRITY_VALIDATION
// .of("Exam currently has active SEB Client connections."));
// }
//
// return exam;
} }
@ -637,38 +619,37 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
} }
} }
private Exam applyConnectionConfiguration(final Exam exam) { // private Exam applyConnectionConfiguration(final Exam exam) {
return lmsAPITemplateCacheService // return lmsAPITemplateCacheService
.getLmsAPITemplate(exam.lmsSetupId) // .getLmsAPITemplate(exam.lmsSetupId)
.flatMap(template -> { // .flatMap(template -> {
final String connectionConfigId = getConnectionConfigurationId(exam); // final String connectionConfigId = getConnectionConfigurationId(exam);
//
final ByteArrayOutputStream out = new ByteArrayOutputStream(); // final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PipedOutputStream pout; // final PipedOutputStream pout;
final PipedInputStream pin; // final PipedInputStream pin;
try { // try {
pout = new PipedOutputStream(); // pout = new PipedOutputStream();
pin = new PipedInputStream(pout); // pin = new PipedInputStream(pout);
//
this.connectionConfigurationService // this.connectionConfigurationService
.exportSEBClientConfiguration(pout, connectionConfigId, exam.id); // .exportSEBClientConfiguration(pout, connectionConfigId, exam.id);
//
out.flush(); // out.flush();
//
IOUtils.copyLarge(pin, out); // IOUtils.copyLarge(pin, out);
//
// TODO check if this works as expected // return template.applyConnectionConfiguration(exam, out.toByteArray());
return template.applyConnectionConfiguration(exam, out.toByteArray()); //
// } catch (final Exception e) {
} catch (final Exception e) { // throw new RuntimeException("Failed to stream output", e);
throw new RuntimeException("Failed to stream output", e); // } finally {
} finally { // IOUtils.closeQuietly(out);
IOUtils.closeQuietly(out); // }
} // })
}) // .onError(error -> log.error("Failed to apply ConnectionConfiguration for exam: {} error: ", exam, error))
.onError(error -> log.error("Failed to apply ConnectionConfiguration for exam: {} error: ", exam, error)) // .getOr(exam);
.getOr(exam); // }
}
private boolean hasFullIntegration(final Long lmsSetupId) { private boolean hasFullIntegration(final Long lmsSetupId) {
// no LMS // no LMS

View file

@ -546,23 +546,6 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
.getOrThrow()); .getOrThrow());
} }
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
if (this.lmsIntegrationAPI == null) {
return Result.ofError(
new UnsupportedOperationException("LMS Integration API Not Supported For: " + getType().name()));
}
if (log.isDebugEnabled()) {
log.debug("Apply Connection Configuration for exam: {} for LMSSetup: {}", exam, lmsSetup());
}
return this.examRequest.protectedRun(() -> this.lmsIntegrationAPI
.applyConnectionConfiguration(exam, configData)
.getOrThrow());
}
@Override @Override
public Result<String> deleteConnectionDetails() { public Result<String> deleteConnectionDetails() {
if (this.lmsIntegrationAPI == null) { if (this.lmsIntegrationAPI == null) {

View file

@ -444,11 +444,6 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
return Result.ofRuntimeError("Not Supported"); return Result.ofRuntimeError("Not Supported");
} }
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override @Override
public Result<String> deleteConnectionDetails() { public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("Not Supported"); return Result.ofRuntimeError("Not Supported");

View file

@ -39,11 +39,6 @@ public class MockupFullIntegration implements FullLmsIntegrationAPI {
return Result.ofRuntimeError("Not Supported"); return Result.ofRuntimeError("Not Supported");
} }
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override @Override
public Result<String> deleteConnectionDetails() { public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("TODO"); return Result.ofRuntimeError("TODO");

View file

@ -452,32 +452,12 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
new HttpEntity<>(body, headers); new HttpEntity<>(body, headers);
final ResponseEntity<String> exchange = super.exchange( final ResponseEntity<String> exchange = super.exchange(
uri.toString(), uri.append("?token=").append(this.accessToken).toString(),
HttpMethod.POST, HttpMethod.POST,
requestEntity, requestEntity,
String.class); String.class);
return exchange.getBody(); return exchange.getBody();
// multiPartAttributes.add("token", this.accessToken.toString());
//
// queryAttributes.forEach((key, values) -> {
// if (values.isEmpty()) {
// return;
// }
// if (uri.toString().contains("?")) {
// uri.append("&").append(key).append("=").append(values.get(0));
// } else {
// uri.append("?").append(key).append("=").append(values.get(0));
// }
// });
//
// log.info("Upload to Moodle url: {}", uri.toString());
//
// return super.postForObject(
// uri.toString(),
// multiPartAttributes,
// String.class);
} }
private String doRequest( private String doRequest(

View file

@ -239,43 +239,43 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
}); });
} }
@Override // @Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) { // public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.tryCatch(() -> { // return Result.tryCatch(() -> {
final String quizId = MoodleUtils.getQuizId(exam.externalId);
final String fileName = getConnectionConfigFileName(exam);
// final MultiValueMap<String, Object> multiPartAttributes = new LinkedMultiValueMap<>();
// multiPartAttributes.add("quizid", quizId);
// multiPartAttributes.add("name", fileName);
// multiPartAttributes.add("filename", fileName);
// final MultiValueMap<String, String> queryAttributes = new LinkedMultiValueMap<>();
// //queryAttributes.add("quizid", quizId);
// final ByteArrayResource contentsAsResource = new ByteArrayResource(configData) {
// @Override
// public String getFilename() {
// return fileName; // Filename has to be returned in order to be able to post.
// }
// };
// //
// multiPartAttributes.add("file", contentsAsResource); // final String quizId = MoodleUtils.getQuizId(exam.externalId);
// final String fileName = getConnectionConfigFileName(exam);
final MoodleAPIRestTemplate rest = getRestTemplate().getOrThrow(); //
final String response = rest.uploadMultiPart( //// final MultiValueMap<String, Object> multiPartAttributes = new LinkedMultiValueMap<>();
UPLOAD_ENDPOINT, //// multiPartAttributes.add("quizid", quizId);
quizId, //// multiPartAttributes.add("name", fileName);
fileName, //// multiPartAttributes.add("filename", fileName);
configData); //
//// final MultiValueMap<String, String> queryAttributes = new LinkedMultiValueMap<>();
if (response != null) { //// //queryAttributes.add("quizid", quizId);
log.info("Upload Connection Configuration to Moodle: quizid: {}, fileName: {} response: {}", quizId, fileName, response ); //// final ByteArrayResource contentsAsResource = new ByteArrayResource(configData) {
} //// @Override
//// public String getFilename() {
return exam; //// return fileName; // Filename has to be returned in order to be able to post.
}); //// }
} //// };
////
//// multiPartAttributes.add("file", contentsAsResource);
//
// final MoodleAPIRestTemplate rest = getRestTemplate().getOrThrow();
// final String response = rest.uploadMultiPart(
// UPLOAD_ENDPOINT,
// quizId,
// fileName,
// configData);
//
// if (response != null) {
// log.info("Upload Connection Configuration to Moodle: quizid: {}, fileName: {} response: {}", quizId, fileName, response );
// }
//
// return exam;
// });
// }
private String getConnectionConfigFileName(final Exam exam) { private String getConnectionConfigFileName(final Exam exam) {
return "SEBServerConnectionConfiguration-" + exam.id + ".seb"; return "SEBServerConnectionConfiguration-" + exam.id + ".seb";
@ -317,7 +317,9 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
public Result<QuizData> getQuizDataForRemoteImport(final String examData) { public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
return Result.tryCatch(() -> { return Result.tryCatch(() -> {
log.info("****** Try to parse import exam data sent by Moodle on Exam import: {}", examData); if (log.isDebugEnabled()) {
log.debug("Try to parse import exam data sent by Moodle on Exam import: {}", examData);
}
final LmsSetup lmsSetup = this.restTemplateFactory.getApiTemplateDataSupplier().getLmsSetup(); final LmsSetup lmsSetup = this.restTemplateFactory.getApiTemplateDataSupplier().getLmsSetup();
final String urlPrefix = (lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR)) final String urlPrefix = (lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR))

View file

@ -431,11 +431,6 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
return Result.ofRuntimeError("Not Supported"); return Result.ofRuntimeError("Not Supported");
} }
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override @Override
public Result<String> deleteConnectionDetails() { public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("Not Supported"); return Result.ofRuntimeError("Not Supported");