Made Exam API asynchronous a, fix tests and fix monitoring activity
This commit is contained in:
parent
a360bfffc8
commit
43578d3e1c
5 changed files with 294 additions and 195 deletions
|
@ -335,10 +335,14 @@ public class ActivitiesPane implements TemplateComposer {
|
|||
.create());
|
||||
}
|
||||
|
||||
monitoring.setExpanded(
|
||||
this.currentUser
|
||||
.get()
|
||||
.hasAnyRole(UserRole.EXAM_SUPPORTER));
|
||||
if (monitoring.getItemCount() > 0) {
|
||||
monitoring.setExpanded(
|
||||
this.currentUser
|
||||
.get()
|
||||
.hasAnyRole(UserRole.EXAM_SUPPORTER));
|
||||
} else {
|
||||
monitoring.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- MONITORING ---------------------------------------------------------------------
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
@ -205,4 +206,24 @@ public class APIExceptionHandler extends ResponseEntityExceptionHandler {
|
|||
HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(CompletionException.class)
|
||||
public ResponseEntity<Object> handleCompletionException(
|
||||
final CompletionException ex,
|
||||
final WebRequest request) {
|
||||
|
||||
final Throwable cause = ex.getCause();
|
||||
if (cause instanceof APIMessageException) {
|
||||
return handleAPIMessageException((APIMessageException) cause, request);
|
||||
} else if (cause instanceof APIConstraintViolationException) {
|
||||
return handleIllegalAPIArgumentException((APIConstraintViolationException) cause, request);
|
||||
} else if (cause instanceof ResourceNotFoundException) {
|
||||
return APIMessage.ErrorMessage.RESOURCE_NOT_FOUND.createErrorResponse(cause.getMessage());
|
||||
} else if (cause instanceof RuntimeException) {
|
||||
return APIMessage.ErrorMessage.UNEXPECTED.createErrorResponse(cause.getMessage());
|
||||
}
|
||||
|
||||
return APIMessage.ErrorMessage.GENERIC.createErrorResponse(cause.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.security.Principal;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -86,7 +87,7 @@ public class ExamAPI_V1_Controller {
|
|||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public Collection<RunningExamInfo> handshakeCreate(
|
||||
public CompletableFuture<Collection<RunningExamInfo>> handshakeCreate(
|
||||
@RequestParam(name = API.PARAM_INSTITUTION_ID, required = false) final Long instIdRequestParam,
|
||||
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examIdRequestParam,
|
||||
@RequestBody(required = false) final MultiValueMap<String, String> formParams,
|
||||
|
@ -94,217 +95,179 @@ public class ExamAPI_V1_Controller {
|
|||
final HttpServletRequest request,
|
||||
final HttpServletResponse response) {
|
||||
|
||||
final POSTMapper mapper = new POSTMapper(formParams);
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = (instIdRequestParam != null)
|
||||
? instIdRequestParam
|
||||
: mapper.getLong(API.PARAM_INSTITUTION_ID);
|
||||
final Long examId = (examIdRequestParam != null)
|
||||
? examIdRequestParam
|
||||
: mapper.getLong(API.EXAM_API_PARAM_EXAM_ID);
|
||||
final POSTMapper mapper = new POSTMapper(formParams);
|
||||
|
||||
// Create and get new ClientConnection if all integrity checks passes
|
||||
final ClientConnection clientConnection = this.sebClientConnectionService
|
||||
.createClientConnection(principal, institutionId, remoteAddr, examId)
|
||||
.getOrThrow();
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = (instIdRequestParam != null)
|
||||
? instIdRequestParam
|
||||
: mapper.getLong(API.PARAM_INSTITUTION_ID);
|
||||
final Long examId = (examIdRequestParam != null)
|
||||
? examIdRequestParam
|
||||
: mapper.getLong(API.EXAM_API_PARAM_EXAM_ID);
|
||||
|
||||
response.setHeader(
|
||||
API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
clientConnection.connectionToken);
|
||||
// Create and get new ClientConnection if all integrity checks passes
|
||||
final ClientConnection clientConnection = this.sebClientConnectionService
|
||||
.createClientConnection(principal, institutionId, remoteAddr, examId)
|
||||
.getOrThrow();
|
||||
|
||||
// Crate list of running exams
|
||||
List<RunningExamInfo> result;
|
||||
if (examId == null) {
|
||||
result = this.examSessionService.getRunningExamsForInstitution(institutionId)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.map(this::createRunningExamInfo)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
final Exam exam = this.examSessionService.getExamDAO().byPK(examId)
|
||||
.getOrThrow();
|
||||
response.setHeader(
|
||||
API.EXAM_API_SEB_CONNECTION_TOKEN,
|
||||
clientConnection.connectionToken);
|
||||
|
||||
result = Arrays.asList(createRunningExamInfo(exam));
|
||||
}
|
||||
// Crate list of running exams
|
||||
List<RunningExamInfo> result;
|
||||
if (examId == null) {
|
||||
result = this.examSessionService.getRunningExamsForInstitution(institutionId)
|
||||
.getOrThrow()
|
||||
.stream()
|
||||
.map(this::createRunningExamInfo)
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
final Exam exam = this.examSessionService.getExamDAO().byPK(examId)
|
||||
.getOrThrow();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
log.warn("There are no currently running exams for institution: {}. SEB connection creation denied",
|
||||
institutionId);
|
||||
throw new IllegalStateException("There are no currently running exams");
|
||||
}
|
||||
result = Arrays.asList(createRunningExamInfo(exam));
|
||||
}
|
||||
|
||||
return result;
|
||||
if (result.isEmpty()) {
|
||||
log.warn(
|
||||
"There are no currently running exams for institution: {}. SEB connection creation denied",
|
||||
institutionId);
|
||||
throw new IllegalStateException("There are no currently running exams");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
this.executor);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
method = RequestMethod.PATCH,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public void handshakeUpdate(
|
||||
public CompletableFuture<Void> handshakeUpdate(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examId,
|
||||
@RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
return CompletableFuture.runAsync(
|
||||
() -> {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection update endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "exam: {} "
|
||||
+ "userSessionId: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
examId,
|
||||
userSessionId,
|
||||
remoteAddr);
|
||||
}
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
|
||||
this.sebClientConnectionService.updateClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr,
|
||||
userSessionId)
|
||||
.getOrThrow();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection update endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "exam: {} "
|
||||
+ "userSessionId: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
examId,
|
||||
userSessionId,
|
||||
remoteAddr);
|
||||
}
|
||||
|
||||
this.sebClientConnectionService.updateClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr,
|
||||
userSessionId)
|
||||
.getOrThrow();
|
||||
},
|
||||
this.executor);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
method = RequestMethod.PUT,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public void handshakeEstablish(
|
||||
public CompletableFuture<Void> handshakeEstablish(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_PARAM_EXAM_ID, required = false) final Long examId,
|
||||
@RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
return CompletableFuture.runAsync(
|
||||
() -> {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection establish endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "exam: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr);
|
||||
}
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
|
||||
this.sebClientConnectionService.establishClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr,
|
||||
userSessionId)
|
||||
.getOrThrow();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection establish endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "exam: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr);
|
||||
}
|
||||
|
||||
this.sebClientConnectionService.establishClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
examId,
|
||||
remoteAddr,
|
||||
userSessionId)
|
||||
.getOrThrow();
|
||||
},
|
||||
this.executor);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
method = RequestMethod.DELETE,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public void handshakeDelete(
|
||||
public CompletableFuture<Void> handshakeDelete(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
return CompletableFuture.runAsync(
|
||||
() -> {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection close endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
remoteAddr);
|
||||
}
|
||||
final String remoteAddr = request.getRemoteAddr();
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
|
||||
this.sebClientConnectionService.closeConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
remoteAddr)
|
||||
.getOrThrow();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Request received on SEB Client Connection close endpoint: "
|
||||
+ "institution: {} "
|
||||
+ "client-address: {}",
|
||||
institutionId,
|
||||
remoteAddr);
|
||||
}
|
||||
|
||||
this.sebClientConnectionService.closeConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
remoteAddr)
|
||||
.getOrThrow();
|
||||
},
|
||||
this.executor);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public void getConfig(
|
||||
public CompletableFuture<Void> getConfig(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(required = false) final MultiValueMap<String, String> formParams,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request,
|
||||
final HttpServletResponse response) throws IOException {
|
||||
|
||||
// if an examId is provided with the request, update the connection first
|
||||
if (formParams != null && formParams.containsKey(API.EXAM_API_PARAM_EXAM_ID)) {
|
||||
final String examId = formParams.getFirst(API.EXAM_API_PARAM_EXAM_ID);
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
final ClientConnection connection = this.sebClientConnectionService.updateClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
Long.valueOf(examId),
|
||||
null,
|
||||
null)
|
||||
.getOrThrow();
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Updated connection: {}", connection);
|
||||
}
|
||||
}
|
||||
|
||||
final ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
||||
try {
|
||||
|
||||
final ClientConnectionData connection = this.examSessionService
|
||||
.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
// exam integrity check
|
||||
if (connection.clientConnection.examId == null ||
|
||||
!this.examSessionService.isExamRunning(connection.clientConnection.examId)) {
|
||||
|
||||
log.error("Missing exam identifer or requested exam is not running for connection: {}", connection);
|
||||
throw new IllegalStateException("Missing exam identider or requested exam is not running");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
|
||||
log.error("Unexpected error: ", e);
|
||||
|
||||
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
|
||||
outputStream.write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
this.examSessionService
|
||||
.streamDefaultExamConfig(
|
||||
connectionToken,
|
||||
outputStream);
|
||||
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
|
||||
} catch (final 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();
|
||||
}
|
||||
return CompletableFuture.runAsync(
|
||||
() -> streamExamConfig(connectionToken, formParams, principal, response),
|
||||
this.executor);
|
||||
}
|
||||
|
||||
private static final ResponseEntity<String> EMPTY_PING_RESPONSE = ResponseEntity
|
||||
|
@ -316,49 +279,38 @@ public class ExamAPI_V1_Controller {
|
|||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public ResponseEntity<String> ping(
|
||||
public CompletableFuture<ResponseEntity<String>> ping(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_PING_TIMESTAMP, required = true) final long timestamp,
|
||||
@RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) {
|
||||
|
||||
final String notifyPing = this.sebClientConnectionService
|
||||
.notifyPing(connectionToken, timestamp, pingNumber);
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final String notifyPing = this.sebClientConnectionService
|
||||
.notifyPing(connectionToken, timestamp, pingNumber);
|
||||
if (notifyPing == null) {
|
||||
return EMPTY_PING_RESPONSE;
|
||||
}
|
||||
|
||||
if (notifyPing == null) {
|
||||
return EMPTY_PING_RESPONSE;
|
||||
}
|
||||
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.body(notifyPing);
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.body(notifyPing);
|
||||
},
|
||||
this.executor);
|
||||
}
|
||||
|
||||
// @RequestMapping(
|
||||
// path = API.EXAM_API_PING_ENDPOINT,
|
||||
// method = RequestMethod.POST,
|
||||
// consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
// produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
// public CompletableFuture<String> ping(
|
||||
// @RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
// @RequestParam(name = API.EXAM_API_PING_TIMESTAMP, required = true) final long timestamp,
|
||||
// @RequestParam(name = API.EXAM_API_PING_NUMBER, required = false) final int pingNumber) {
|
||||
//
|
||||
// return CompletableFuture.supplyAsync(
|
||||
// () -> this.sebClientConnectionService
|
||||
// .notifyPing(connectionToken, timestamp, pingNumber),
|
||||
// this.executor);
|
||||
// }
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_EVENT_ENDPOINT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public void event(
|
||||
public CompletableFuture<Void> event(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestBody(required = true) final ClientEvent event) {
|
||||
|
||||
this.sebClientConnectionService
|
||||
.notifyClientEvent(connectionToken, event);
|
||||
return CompletableFuture.runAsync(
|
||||
() -> this.sebClientConnectionService
|
||||
.notifyClientEvent(connectionToken, event),
|
||||
this.executor);
|
||||
}
|
||||
|
||||
private Long getInstitutionId(final Principal principal) {
|
||||
|
@ -375,4 +327,91 @@ public class ExamAPI_V1_Controller {
|
|||
.getOr(null));
|
||||
}
|
||||
|
||||
private void streamExamConfig(
|
||||
final String connectionToken,
|
||||
final MultiValueMap<String, String> formParams,
|
||||
final Principal principal,
|
||||
final HttpServletResponse response) {
|
||||
|
||||
try {
|
||||
|
||||
// if an examId is provided with the request, update the connection first
|
||||
if (formParams != null && formParams.containsKey(API.EXAM_API_PARAM_EXAM_ID)) {
|
||||
final String examId = formParams.getFirst(API.EXAM_API_PARAM_EXAM_ID);
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
final ClientConnection connection = this.sebClientConnectionService.updateClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
Long.valueOf(examId),
|
||||
null,
|
||||
null)
|
||||
.getOrThrow();
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Updated connection: {}", connection);
|
||||
}
|
||||
}
|
||||
|
||||
final ServletOutputStream outputStream = response.getOutputStream();
|
||||
|
||||
try {
|
||||
|
||||
final ClientConnectionData connection = this.examSessionService
|
||||
.getConnectionData(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
// exam integrity check
|
||||
if (connection.clientConnection.examId == null ||
|
||||
!this.examSessionService.isExamRunning(connection.clientConnection.examId)) {
|
||||
|
||||
log.error("Missing exam identifier or requested exam is not running for connection: {}",
|
||||
connection);
|
||||
throw new IllegalStateException("Missing exam identifier or requested exam is not running");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
|
||||
log.error("Unexpected error: ", e);
|
||||
|
||||
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
|
||||
outputStream.write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
|
||||
response.setStatus(HttpStatus.BAD_REQUEST.value());
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
this.examSessionService
|
||||
.streamDefaultExamConfig(
|
||||
connectionToken,
|
||||
outputStream);
|
||||
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
|
||||
} catch (final 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();
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to stream SEB Exam Configuration to client with connection: {}",
|
||||
connectionToken,
|
||||
e);
|
||||
|
||||
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
try {
|
||||
response.getOutputStream().write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
|
||||
} catch (final Exception e1) {
|
||||
log.error("Failed to write error to response: ", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.integration.api.exam;
|
|||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -39,8 +40,10 @@ import org.springframework.security.web.FilterChainProxy;
|
|||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultActions;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
@ -153,7 +156,12 @@ public abstract class ExamAPIIntegrationTester {
|
|||
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder)
|
||||
final MvcResult mvcResult = this.mockMvc.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult))
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
|
@ -209,7 +217,13 @@ public abstract class ExamAPIIntegrationTester {
|
|||
}
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
final MvcResult mvcResult = this.mockMvc
|
||||
.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
@ -221,7 +235,13 @@ public abstract class ExamAPIIntegrationTester {
|
|||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
|
||||
final MvcResult mvcResult = this.mockMvc
|
||||
.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
|
@ -239,7 +259,12 @@ public abstract class ExamAPIIntegrationTester {
|
|||
+ "&" + API.EXAM_API_PING_NUMBER + "=" + num;
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
final MvcResult mvcResult = this.mockMvc
|
||||
.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
|
@ -259,7 +284,12 @@ public abstract class ExamAPIIntegrationTester {
|
|||
|
||||
final String body = "{ \"type\": \"%s\", \"timestamp\": %s, \"numericValue\": %s, \"text\": \"%s\" }";
|
||||
builder.content(String.format(body, type, timestamp, value, text));
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
final MvcResult mvcResult = this.mockMvc
|
||||
.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
|
@ -278,8 +308,13 @@ public abstract class ExamAPIIntegrationTester {
|
|||
builder.content("examId=" + examId);
|
||||
}
|
||||
|
||||
final ResultActions result = this.mockMvc
|
||||
.perform(builder);
|
||||
final MvcResult mvcResult = this.mockMvc
|
||||
.perform(builder)
|
||||
.andExpect(request().asyncStarted())
|
||||
.andDo(MockMvcResultHandlers.log())
|
||||
.andReturn();
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(asyncDispatch(mvcResult));
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ public class SebExamConfigurationRequestTest extends ExamAPIIntegrationTester {
|
|||
// check error
|
||||
final String contentAsString = configResponse.getContentAsString();
|
||||
assertEquals(
|
||||
"{\"messageCode\":\"0\",\"systemMessage\":\"Generic error message\",\"details\":\"Missing exam identider or requested exam is not running\",\"attributes\":[]}",
|
||||
"{\"messageCode\":\"0\",\"systemMessage\":\"Generic error message\",\"details\":\"Missing exam identifier or requested exam is not running\",\"attributes\":[]}",
|
||||
contentAsString);
|
||||
|
||||
// check connection cache
|
||||
|
@ -253,7 +253,7 @@ public class SebExamConfigurationRequestTest extends ExamAPIIntegrationTester {
|
|||
assertTrue(HttpStatus.OK.value() != configResponse.getStatus());
|
||||
final String contentAsString = configResponse.getContentAsString();
|
||||
assertEquals(
|
||||
"{\"messageCode\":\"0\",\"systemMessage\":\"Generic error message\",\"details\":\"Missing exam identider or requested exam is not running\",\"attributes\":[]}",
|
||||
"{\"messageCode\":\"0\",\"systemMessage\":\"Generic error message\",\"details\":\"Missing exam identifier or requested exam is not running\",\"attributes\":[]}",
|
||||
contentAsString);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue