SEBSLI-9 create new endpoint for light setup to stream exam config

This commit is contained in:
Nadim Ritter 2024-02-21 16:10:59 +01:00
parent 074b63580b
commit cecce64ba0
5 changed files with 173 additions and 27 deletions

View file

@ -84,6 +84,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements E
.antMatchers(ERROR_PATH)
.antMatchers(CHECK_PATH)
.antMatchers(this.examAPIDiscoveryEndpoint)
.antMatchers(this.examAPIDiscoveryEndpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT)
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.LOGO_PATH_SEGMENT + "/**")
.antMatchers(this.adminAPIEndpoint + API.INFO_ENDPOINT + API.INFO_INST_PATH_SEGMENT + "/**")
.antMatchers(this.adminAPIEndpoint + API.REGISTER_ENDPOINT);

View file

@ -123,6 +123,8 @@ public final class API {
public static final String EXAM_API_CONFIGURATION_REQUEST_ENDPOINT = "/examconfig";
public static final String EXAM_API_CONFIGURATION_LIGHT_ENDPOINT = "/light-config";
public static final String EXAM_API_PING_ENDPOINT = "/sebping";
public static final String EXAM_API_PING_TIMESTAMP = "timestamp";

View file

@ -9,6 +9,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.util.Collection;
@ -173,4 +174,8 @@ public interface SEBClientConnectionService {
String ipAddress,
HttpServletResponse response);
void streamLightExamConfig(
String modelId,
HttpServletResponse response) throws IOException;
}

View file

@ -8,23 +8,36 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.security.Principal;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ScreenProctoringSettings;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ClientConfigService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedIndicatorValueService;
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@ -34,25 +47,20 @@ import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig.VDIType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.institution.SecurityKeyService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.DistributedIndicatorValueService;
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Lazy
@Service
@ -79,6 +87,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
private final SecurityKeyService securityKeyService;
private final SEBClientEventBatchService sebClientEventBatchService;
private final SEBClientInstructionService sebClientInstructionService;
private final ClientConfigService clientConfigService;
private final JSONMapper jsonMapper;
private final boolean isDistributedSetup;
@ -92,6 +101,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
final WebserviceInfo webserviceInfo,
final SEBClientEventBatchService sebClientEventBatchService,
final SEBClientInstructionService sebClientInstructionService,
final ClientConfigService clientConfigService,
final JSONMapper jsonMapper) {
this.examSessionService = examSessionService;
@ -105,6 +115,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
this.isDistributedSetup = webserviceInfo.isDistributed();
this.sebClientEventBatchService = sebClientEventBatchService;
this.sebClientInstructionService = sebClientInstructionService;
this.clientConfigService = clientConfigService;
this.jsonMapper = jsonMapper;
}
@ -623,6 +634,38 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
}
}
public void streamLightExamConfig(final String modelId, final HttpServletResponse response) throws IOException{
final ServletOutputStream outputStream = response.getOutputStream();
PipedOutputStream pout;
PipedInputStream pin;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.clientConfigService.exportSEBClientConfiguration(
pout,
modelId,
null);
IOUtils.copyLarge(pin, outputStream);
response.setStatus(HttpStatus.OK.value());
outputStream.flush();
}catch(Exception e){
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
outputStream.write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
} finally {
outputStream.flush();
outputStream.close();
}
}
private void writeSEBClientErrors(
final HttpServletResponse response,
final Collection<APIMessage> errorMessages) {

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services / Informatikdienste (ID)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.weblayer.api;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.async.AsyncServiceSpringConfig;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SEBClientConfigDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@WebServiceProfile
@RestController
@RequestMapping("${sebserver.webservice.api.exam.endpoint.discovery}")
@ConditionalOnExpression("'${sebserver.webservice.light.setup}'.equals('true')")
public class ExamAPIDiscoveryLightController {
private final SEBClientConnectionService sebClientConnectionService;
private final SEBClientConfigDAO sebClientConfigDAO;
private final Executor executor;
protected ExamAPIDiscoveryLightController(
final SEBClientConnectionService sebClientConnectionService,
final SEBClientConfigDAO sebClientConfigDAO,
@Qualifier(AsyncServiceSpringConfig.EXAM_API_EXECUTOR_BEAN_NAME) final Executor executor) {
this.sebClientConnectionService = sebClientConnectionService;
this.sebClientConfigDAO = sebClientConfigDAO;
this.executor = executor;
}
//this.examAPI_V1_Endpoint + API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT
//http://localhost:8080/exam-api/discovery/light-config
@RequestMapping(
path = API.EXAM_API_CONFIGURATION_LIGHT_ENDPOINT,
method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public CompletableFuture<Void> getLightConfig(
final HttpServletRequest request,
final HttpServletResponse response){
//temp solution: get first active seb client config we can get -->
//in a light setup there should be only one setup so this step is not necessary and we can just use the first and only item in the db
String modelId = getSebClientConfigId();
return CompletableFuture.runAsync(
() -> {
try {
this.sebClientConnectionService.streamLightExamConfig(modelId, response);
} catch (IOException e) {
throw new RuntimeException(e);
}
},
this.executor
);
}
private String getSebClientConfigId() {
return this.sebClientConfigDAO
.allMatching(
new FilterMap().putIfAbsent(
"active",
String.valueOf(true)
),
Utils.truePredicate()
)
.getOrThrow()
.stream()
.collect(Collectors.toList())
.get(0)
.getModelId();
}
}