SEBSERV-62 testing and fixes
This commit is contained in:
parent
b314ca651f
commit
3b6e3d88e0
29 changed files with 1201 additions and 191 deletions
|
@ -152,7 +152,7 @@ public class APIMessage implements Serializable {
|
|||
|
||||
/** Use this as a conversion from a given FieldError of Spring to a APIMessage
|
||||
* of type field validation.
|
||||
*
|
||||
*
|
||||
* @param error FieldError instance
|
||||
* @return converted APIMessage of type field validation */
|
||||
public static final APIMessage fieldValidationError(final FieldError error) {
|
||||
|
|
|
@ -55,12 +55,6 @@ public interface ClientCredentialService {
|
|||
return encryptClientCredentials(clientIdPlaintext, secretPlaintext, null);
|
||||
}
|
||||
|
||||
// /** Use this to get a decrypted plain text clientId form given ClientCredentials
|
||||
// *
|
||||
// * @param credentials ClientCredentials containing the clientId to decrypt
|
||||
// * @return decrypted plain text clientId */
|
||||
// CharSequence getPlainClientId(ClientCredentials credentials);
|
||||
|
||||
/** Use this to get a decrypted plain text secret form given ClientCredentials
|
||||
*
|
||||
* @param credentials ClientCredentials containing the secret to decrypt
|
||||
|
|
|
@ -61,7 +61,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
final CharSequence accessTokenPlaintext) {
|
||||
|
||||
final CharSequence secret = this.environment
|
||||
.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
|
||||
return new ClientCredentials(
|
||||
clientIdPlaintext,
|
||||
|
@ -73,11 +73,6 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
: null);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public CharSequence getPlainClientId(final ClientCredentials credentials) {
|
||||
// return credentials.clientId;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public CharSequence getPlainClientSecret(final ClientCredentials credentials) {
|
||||
if (credentials == null || !credentials.hasSecret()) {
|
||||
|
@ -85,7 +80,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
}
|
||||
|
||||
final CharSequence secret = this.environment
|
||||
.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
return this.decrypt(credentials.secret, secret);
|
||||
}
|
||||
|
||||
|
@ -96,7 +91,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
}
|
||||
|
||||
final CharSequence secret = this.environment
|
||||
.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
|
||||
return this.decrypt(credentials.accessToken, secret);
|
||||
}
|
||||
|
@ -105,7 +100,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
public CharSequence encrypt(final CharSequence text) {
|
||||
|
||||
final CharSequence secret = this.environment
|
||||
.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
|
||||
return encrypt(text, secret);
|
||||
}
|
||||
|
@ -114,7 +109,7 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
public CharSequence decrypt(final CharSequence text) {
|
||||
|
||||
final CharSequence secret = this.environment
|
||||
.getRequiredProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
.getProperty(SEBSERVER_WEBSERVICE_INTERNAL_SECRET_KEY);
|
||||
|
||||
return decrypt(text, secret);
|
||||
}
|
||||
|
@ -124,6 +119,11 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
throw new IllegalArgumentException("Text has null reference");
|
||||
}
|
||||
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip encryption");
|
||||
return text;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final CharSequence salt = KeyGenerators.string().generateKey();
|
||||
|
@ -145,6 +145,11 @@ public class ClientCredentialServiceImpl implements ClientCredentialService {
|
|||
throw new IllegalArgumentException("Cipher has null reference");
|
||||
}
|
||||
|
||||
if (secret == null) {
|
||||
log.warn("No internal secret supplied: skip decryption");
|
||||
return cipher;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final int length = cipher.length();
|
||||
|
|
|
@ -8,13 +8,25 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
public interface ClientConnectionDAO extends EntityDAO<ClientConnection, ClientConnection> {
|
||||
|
||||
Result<ClientConnection> byConnectionToken(Long institutionId, String connectionToken);
|
||||
/** Get a list of all connection tokens of all connections (no matter what state)
|
||||
* of an exam.
|
||||
*
|
||||
* @param examId The exam identifier
|
||||
* @return list of all connection tokens of all connections (no matter what state)
|
||||
* of an exam */
|
||||
Result<Collection<String>> getConnectionTokens(Long examId);
|
||||
|
||||
/** Get a ClientConnection for a specified token.
|
||||
*
|
||||
* @param connectionToken the connection token
|
||||
* @return Result refer to ClientConnection or refer to a error if happened */
|
||||
Result<ClientConnection> byConnectionToken(String connectionToken);
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,21 @@ public interface ExamConfigurationMapDAO extends
|
|||
EntityDAO<ExamConfigurationMap, ExamConfigurationMap>,
|
||||
BulkActionSupportDAO<ExamConfigurationMap> {
|
||||
|
||||
/** Get the ConfigurationNode identifier of the default Exam Configuration of
|
||||
* the Exam with specified identifier.
|
||||
*
|
||||
* @param examId The Exam identifier
|
||||
* @return ConfigurationNode identifier of the default Exam Configuration of
|
||||
* the Exam with specified identifier */
|
||||
public Result<Long> getDefaultConfigurationForExam(Long examId);
|
||||
|
||||
/** Get the ConfigurationNode identifier of the Exam Configuration of
|
||||
* the Exam for a specified user identifier.
|
||||
*
|
||||
* @param examId The Exam identifier
|
||||
* @param userId the user identifier
|
||||
* @return ConfigurationNode identifier of the Exam Configuration of
|
||||
* the Exam for a specified user identifier */
|
||||
public Result<Long> getUserConfigurationIdForExam(final Long examId, final String userId);
|
||||
|
||||
}
|
||||
|
|
|
@ -110,6 +110,24 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<Collection<String>> getConnectionTokens(final Long examId) {
|
||||
return Result.tryCatch(() -> {
|
||||
return this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.where(
|
||||
ClientConnectionRecordDynamicSqlSupport.examId,
|
||||
SqlBuilder.isEqualTo(examId))
|
||||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(ClientConnectionRecord::getConnectionToken)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<ClientConnection> createNew(final ClientConnection data) {
|
||||
|
@ -140,10 +158,10 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
final ClientConnectionRecord updateRecord = new ClientConnectionRecord(
|
||||
data.id,
|
||||
null,
|
||||
null,
|
||||
data.examId,
|
||||
data.status != null ? data.status.name() : null,
|
||||
data.connectionToken,
|
||||
null,
|
||||
data.userSessionId,
|
||||
data.clientAddress,
|
||||
data.virtualClientAddress);
|
||||
|
||||
|
@ -183,37 +201,6 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<ClientConnection> byConnectionToken(
|
||||
final Long institutionId,
|
||||
final String connectionToken) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final List<ClientConnectionRecord> list = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.where(
|
||||
ClientConnectionRecordDynamicSqlSupport.institutionId,
|
||||
SqlBuilder.isEqualTo(institutionId))
|
||||
.and(
|
||||
ClientConnectionRecordDynamicSqlSupport.connectionToken,
|
||||
SqlBuilder.isEqualTo(connectionToken))
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw new ResourceNotFoundException(EntityType.CLIENT_CONNECTION, "connectionToken");
|
||||
}
|
||||
|
||||
if (list.size() > 1) {
|
||||
throw new IllegalStateException("Only one ClientConnection expected but there are: " + list.size());
|
||||
}
|
||||
|
||||
return list.get(0);
|
||||
})
|
||||
.flatMap(ClientConnectionDAOImpl::toDomainModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<ClientConnection> byConnectionToken(final String connectionToken) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
|
|
@ -118,7 +118,7 @@ public class ClientEventDAOImpl implements ClientEventDAO {
|
|||
(data.numValue != null) ? new BigDecimal(data.numValue) : null,
|
||||
data.text);
|
||||
|
||||
this.clientEventRecordMapper.insert(newRecord);
|
||||
this.clientEventRecordMapper.insertSelective(newRecord);
|
||||
return newRecord;
|
||||
})
|
||||
.flatMap(ClientEventDAOImpl::toDomainModel)
|
||||
|
|
|
@ -130,7 +130,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
|||
@Transactional(readOnly = true)
|
||||
public Result<Long> getDefaultConfigurationForExam(final Long examId) {
|
||||
return Result.tryCatch(() -> this.examConfigurationMapRecordMapper
|
||||
.selectIdsByExample()
|
||||
.selectByExample()
|
||||
.where(
|
||||
ExamConfigurationMapRecordDynamicSqlSupport.examId,
|
||||
SqlBuilder.isEqualTo(examId))
|
||||
|
@ -140,13 +140,14 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
|||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(mapping -> mapping.getConfigurationNodeId())
|
||||
.collect(Utils.toSingleton()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Long> getUserConfigurationIdForExam(final Long examId, final String userId) {
|
||||
return Result.tryCatch(() -> this.examConfigurationMapRecordMapper
|
||||
.selectIdsByExample()
|
||||
.selectByExample()
|
||||
.where(
|
||||
ExamConfigurationMapRecordDynamicSqlSupport.examId,
|
||||
SqlBuilder.isEqualTo(examId))
|
||||
|
@ -156,6 +157,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
|||
.build()
|
||||
.execute()
|
||||
.stream()
|
||||
.map(mapping -> mapping.getConfigurationNodeId())
|
||||
.collect(Utils.toSingleton()));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.io.OutputStream;
|
|||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValues;
|
||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
/** The base interface and service for all SEB Exam Configuration related functionality. */
|
||||
public interface SebExamConfigService {
|
||||
|
@ -32,10 +31,6 @@ public interface SebExamConfigService {
|
|||
* @throws FieldValidationException on validation exception */
|
||||
void validate(ConfigurationTableValues tableValue) throws FieldValidationException;
|
||||
|
||||
Result<Long> getDefaultConfigurationIdForExam(Long examId);
|
||||
|
||||
Result<Long> getUserConfigurationIdForExam(Long examId, String userId);
|
||||
|
||||
/** Used to export a specified SEB Exam Configuration as plain XML
|
||||
* This exports the values of the follow-up configuration defined by a given
|
||||
* ConfigurationNode (configurationNodeId)
|
||||
|
@ -45,21 +40,26 @@ public interface SebExamConfigService {
|
|||
* @param configurationNodeId the identifier of the ConfigurationNode to export */
|
||||
void exportPlainXML(OutputStream out, Long institutionId, Long configurationNodeId);
|
||||
|
||||
/** Used to export a SEB Exam Configuration within its defined Configuration Exam Mapping.
|
||||
/** Used to export the default SEB Exam Configuration for a given exam identifier.
|
||||
* either with encryption if defined or as plain text within the SEB Configuration format
|
||||
* as described here: https://www.safeexambrowser.org/developer/seb-file-format.html
|
||||
*
|
||||
* @param out The output stream to write the export data to
|
||||
* @param configExamMappingId The identifier of the Exam Configuration Mapping */
|
||||
void exportForExam(OutputStream out, Long configExamMappingId);
|
||||
* @param institutionId The identifier of the institution of the requesting user
|
||||
* @param examId the exam identifier */
|
||||
default Long exportForExam(final OutputStream out, final Long institutionId, final Long examId) {
|
||||
return exportForExam(out, institutionId, examId, null);
|
||||
}
|
||||
|
||||
/** Used to export the default SEB Exam Configuration for a given exam identifier.
|
||||
* either with encryption if defined or as plain text within the SEB Configuration format
|
||||
* as described here: https://www.safeexambrowser.org/developer/seb-file-format.html
|
||||
*
|
||||
* @param out The output stream to write the export data to
|
||||
* @param examId the exam identifier */
|
||||
void exportDefaultForExam(OutputStream out, Long examId);
|
||||
* @param institutionId The identifier of the institution of the requesting user
|
||||
* @param examId the exam identifier
|
||||
* @param userId the user identifier if a specific user based configuration shall be exported */
|
||||
Long exportForExam(OutputStream out, Long institutionId, Long examId, String userId);
|
||||
|
||||
/** TODO */
|
||||
String generateConfigKey(Long configurationNodeId);
|
||||
|
|
|
@ -58,7 +58,11 @@ public class ExamConfigIO {
|
|||
}
|
||||
|
||||
@Async(AsyncServiceSpringConfig.EXECUTOR_BEAN_NAME)
|
||||
void exportPlainXML(final OutputStream out, final Long institutionId, final Long configurationNodeId) {
|
||||
void exportPlainXML(
|
||||
final OutputStream out,
|
||||
final Long institutionId,
|
||||
final Long configurationNodeId) {
|
||||
|
||||
// get all defined root configuration attributes
|
||||
final Map<Long, ConfigurationAttribute> attributes = this.configurationAttributeDAO.getAllRootAttributes()
|
||||
.getOrThrow()
|
||||
|
@ -112,7 +116,7 @@ public class ExamConfigIO {
|
|||
out.write(Constants.XML_PLIST_END_UTF_8);
|
||||
out.flush();
|
||||
|
||||
} catch (final IOException e) {
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to write SEB Exam Configuration XML to output stream: ", e);
|
||||
try {
|
||||
out.flush();
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.io.PipedOutputStream;
|
|||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
@ -96,13 +97,16 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
pout = new PipedOutputStream();
|
||||
pin = new PipedInputStream(pout);
|
||||
|
||||
this.examConfigIO.exportPlainXML(pout, institutionId, configurationNodeId);
|
||||
this.examConfigIO.exportPlainXML(
|
||||
pout,
|
||||
institutionId,
|
||||
configurationNodeId);
|
||||
|
||||
IOUtils.copyLarge(pin, out);
|
||||
|
||||
pin.close();
|
||||
pout.flush();
|
||||
pout.close();
|
||||
pin.close();
|
||||
|
||||
} catch (final IOException e) {
|
||||
log.error("Error while stream plain text SEB clonfiguration data: ", e);
|
||||
|
@ -127,26 +131,32 @@ public class SebExamConfigServiceImpl implements SebExamConfigService {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Long> getDefaultConfigurationIdForExam(final Long examId) {
|
||||
return this.examConfigurationMapDAO.getDefaultConfigurationForExam(examId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Long> getUserConfigurationIdForExam(final Long examId, final String userId) {
|
||||
return this.examConfigurationMapDAO.getUserConfigurationIdForExam(examId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportForExam(final OutputStream out, final Long configExamMappingId) {
|
||||
// TODO Auto-generated method stub
|
||||
public Long exportForExam(
|
||||
final OutputStream out,
|
||||
final Long institutionId,
|
||||
final Long examId,
|
||||
final String userId) {
|
||||
|
||||
}
|
||||
final Long configurationNodeId = (StringUtils.isBlank(userId))
|
||||
? getDefaultConfigurationIdForExam(examId)
|
||||
.getOrThrow()
|
||||
: getUserConfigurationIdForExam(examId, userId)
|
||||
.getOrThrow();
|
||||
|
||||
@Override
|
||||
public void exportDefaultForExam(final OutputStream out, final Long examId) {
|
||||
// TODO Auto-generated method stub
|
||||
// TODO add header, zip and encrypt if needed
|
||||
|
||||
this.exportPlainXML(out, institutionId, configurationNodeId);
|
||||
|
||||
return configurationNodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -65,5 +65,4 @@ public class IntegerConverter implements XMLValueConverter {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,4 +20,8 @@ public interface EventHandlingStrategy extends Consumer<ClientEvent> {
|
|||
String EVENT_CONSUMER_STRATEGY_SINGLE_EVENT_STORE = "SINGLE_EVENT_STORE_STRATEGY";
|
||||
String EVENT_CONSUMER_STRATEGY_ASYNC_BATCH_STORE = "ASYNC_BATCH_STORE_STRATEGY";
|
||||
|
||||
void enable();
|
||||
|
||||
void disable();
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ public interface ExamSessionService {
|
|||
* happened. */
|
||||
Result<Collection<Exam>> getRunningExamsForInstitution(Long institutionId);
|
||||
|
||||
void streamDefaultExamConfig(Long institutionId, String connectionToken, OutputStream out);
|
||||
/** Streams the default SEB Exam Configuration to a ClientConnection with given connectionToken.
|
||||
*
|
||||
* @param connectionToken The connection token that identifiers the ClientConnection
|
||||
* @param out The OutputStream to stream the data to */
|
||||
void streamDefaultExamConfig(String connectionToken, OutputStream out);
|
||||
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ public interface SebClientConnectionService {
|
|||
Result<ClientConnection> updateClientConnection(
|
||||
String connectionToken,
|
||||
Long institutionId,
|
||||
String clientAddress,
|
||||
Long examId,
|
||||
String clientAddress,
|
||||
String userSessionId);
|
||||
|
||||
/** This is used to establish a already created ClientConnection and set it to sate: ESTABLISHED
|
||||
|
|
|
@ -51,7 +51,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
|||
|
||||
@Override
|
||||
public double getValue() {
|
||||
if (this.currentValue == Double.NaN || !this.cachingEnabled) {
|
||||
if (Double.isNaN(this.currentValue) || !this.cachingEnabled) {
|
||||
this.currentValue = computeValueAt(DateTime.now(DateTimeZone.UTC).getMillis());
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.Collections;
|
|||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
|
||||
public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
||||
|
@ -37,4 +39,14 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
|||
return this.EMPTY_SET;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public int getPingCount() {
|
||||
return this.pingCount;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public int getPingNumber() {
|
||||
return this.pingNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
|
|||
|
||||
private final BlockingDeque<ClientEvent> eventQueue = new LinkedBlockingDeque<>();
|
||||
private boolean workersRunning = false;
|
||||
private boolean enabled = false;
|
||||
|
||||
public AsyncBatchEventSaveStrategy(
|
||||
final SqlSessionFactory sqlSessionFactory,
|
||||
|
@ -75,9 +76,22 @@ public class AsyncBatchEventSaveStrategy implements EventHandlingStrategy {
|
|||
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.enabled = true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
protected void recover() {
|
||||
runWorkers();
|
||||
if (this.enabled) {
|
||||
runWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -52,6 +52,10 @@ public class ClientIndicatorFactory {
|
|||
public Collection<ClientIndicator> createFor(final ClientConnection clientConnection) {
|
||||
final List<ClientIndicator> result = new ArrayList<>();
|
||||
|
||||
if (clientConnection.examId == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
final Collection<Indicator> examIndicators = this.indicatorDAO
|
||||
|
|
|
@ -8,13 +8,8 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
|
||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
|
@ -24,7 +19,6 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
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.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||
|
@ -63,7 +57,7 @@ public class ExamSessionCacheService {
|
|||
cacheNames = CACHE_NAME_RUNNING_EXAM,
|
||||
key = "#examId",
|
||||
unless = "#result == null")
|
||||
Exam getRunningExam(final Long examId) {
|
||||
public Exam getRunningExam(final Long examId) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Verify running exam for id: {}" + examId);
|
||||
|
@ -87,7 +81,7 @@ public class ExamSessionCacheService {
|
|||
cacheNames = CACHE_NAME_RUNNING_EXAM,
|
||||
key = "#exam.id",
|
||||
condition = "#target.isRunning(#result)")
|
||||
Exam evict(final Exam exam) {
|
||||
public Exam evict(final Exam exam) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Conditional eviction of running Exam from cache: {}", isRunning(exam));
|
||||
|
@ -108,7 +102,7 @@ public class ExamSessionCacheService {
|
|||
cacheNames = CACHE_NAME_ACTIVE_CLIENT_CONNECTION,
|
||||
key = "#connectionToken",
|
||||
unless = "#result == null")
|
||||
ClientConnectionDataInternal getActiveClientConnection(final String connectionToken) {
|
||||
public ClientConnectionDataInternal getActiveClientConnection(final String connectionToken) {
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Verify ClientConnection for running exam for caching by connectionToken: ", connectionToken);
|
||||
|
@ -123,20 +117,6 @@ public class ExamSessionCacheService {
|
|||
}
|
||||
|
||||
final ClientConnection clientConnection = byPK.get();
|
||||
|
||||
// verify connection is established
|
||||
if (clientConnection.status != ConnectionStatus.ESTABLISHED) {
|
||||
log.error("Illegal state: ClientConnection is not in expected state; ESTABLISHED. ClientConnection: ",
|
||||
clientConnection);
|
||||
return null;
|
||||
}
|
||||
|
||||
// verify exam is running
|
||||
if (getRunningExam(clientConnection.examId) == null) {
|
||||
log.error("Exam for ClientConnection with id { is not currently running}", clientConnection.id);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ClientConnectionDataInternal(
|
||||
clientConnection,
|
||||
this.clientIndicatorFactory.createFor(clientConnection));
|
||||
|
@ -145,7 +125,7 @@ public class ExamSessionCacheService {
|
|||
@CacheEvict(
|
||||
cacheNames = CACHE_NAME_ACTIVE_CLIENT_CONNECTION,
|
||||
key = "#connectionToken")
|
||||
void evictClientConnection(final String connectionToken) {
|
||||
public void evictClientConnection(final String connectionToken) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Eviction of ClientConnectionData from cache: {}", connectionToken);
|
||||
}
|
||||
|
@ -155,25 +135,20 @@ public class ExamSessionCacheService {
|
|||
cacheNames = CACHE_NAME_SEB_CONFIG_EXAM,
|
||||
key = "#examId",
|
||||
unless = "#result == null")
|
||||
InMemorySebConfig getDefaultSebConfigForExam(final Long examId) {
|
||||
public InMemorySebConfig getDefaultSebConfigForExam(final Long examId) {
|
||||
final Exam runningExam = this.getRunningExam(examId);
|
||||
final PipedOutputStream pipOut = new PipedOutputStream();
|
||||
|
||||
try {
|
||||
final Long configId = this.sebExamConfigService
|
||||
.getDefaultConfigurationIdForExam(runningExam.id)
|
||||
.getOrThrow();
|
||||
|
||||
// TODO add header, zip and encrypt if needed
|
||||
|
||||
final BufferedInputStream in = new BufferedInputStream(new PipedInputStream(pipOut));
|
||||
this.sebExamConfigService.exportPlainXML(pipOut, runningExam.institutionId, configId);
|
||||
|
||||
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
IOUtils.copyLarge(in, byteOut);
|
||||
final Long configId = this.sebExamConfigService.exportForExam(
|
||||
byteOut,
|
||||
runningExam.institutionId,
|
||||
examId);
|
||||
|
||||
return new InMemorySebConfig(configId, runningExam.id, byteOut.toByteArray());
|
||||
|
||||
} catch (final IOException e) {
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while getting default exam configuration for running exam; {}", runningExam, e);
|
||||
return null;
|
||||
}
|
||||
|
@ -182,7 +157,7 @@ public class ExamSessionCacheService {
|
|||
@CacheEvict(
|
||||
cacheNames = CACHE_NAME_SEB_CONFIG_EXAM,
|
||||
key = "#examId")
|
||||
void evictDefaultSebConfig(final Long examId) {
|
||||
public void evictDefaultSebConfig(final Long examId) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Eviction of default SEB Configuration from cache for exam: {}", examId);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -71,7 +72,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
return Result.of(exam);
|
||||
} else {
|
||||
if (exam != null) {
|
||||
this.examSessionCacheService.evict(exam);
|
||||
flushCache(exam);
|
||||
}
|
||||
|
||||
log.warn("Exam {} is not currently running", examId);
|
||||
|
@ -92,7 +93,6 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
|
||||
@Override
|
||||
public void streamDefaultExamConfig(
|
||||
final Long institutionId,
|
||||
final String connectionToken,
|
||||
final OutputStream out) {
|
||||
|
||||
|
@ -101,7 +101,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
}
|
||||
|
||||
final ClientConnection connection = this.clientConnectionDAO
|
||||
.byConnectionToken(institutionId, connectionToken)
|
||||
.byConnectionToken(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
if (connection == null || connection.status != ConnectionStatus.ESTABLISHED) {
|
||||
|
@ -111,17 +111,15 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("SEB exam configuration download request: {}", connection);
|
||||
log.debug("Trying to get exam form InMemorySebConfig");
|
||||
log.debug("Trying to get exam from InMemorySebConfig");
|
||||
}
|
||||
|
||||
final InMemorySebConfig sebConfigForExam = this.examSessionCacheService
|
||||
.getDefaultSebConfigForExam(connection.examId);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
if (sebConfigForExam == null) {
|
||||
log.debug("Failed to get and cache InMemorySebConfig for connection: {}", connection);
|
||||
}
|
||||
if (sebConfigForExam == null) {
|
||||
log.error("Failed to get and cache InMemorySebConfig for connection: {}", connection);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -141,4 +139,13 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
|||
}
|
||||
}
|
||||
|
||||
private void flushCache(final Exam exam) {
|
||||
this.examSessionCacheService.evict(exam);
|
||||
this.examSessionCacheService.evictDefaultSebConfig(exam.id);
|
||||
this.clientConnectionDAO
|
||||
.getConnectionTokens(exam.id)
|
||||
.getOrElse(() -> Collections.emptyList())
|
||||
.forEach(token -> this.examSessionCacheService.evictClientConnection(token));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
this.eventHandlingStrategy = applicationContext.getBean(
|
||||
eventHandlingStrategyProperty,
|
||||
EventHandlingStrategy.class);
|
||||
this.eventHandlingStrategy.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,15 +91,23 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
null,
|
||||
institutionId,
|
||||
examId,
|
||||
ClientConnection.ConnectionStatus.CONNECTION_REQUESTED,
|
||||
ConnectionStatus.CONNECTION_REQUESTED,
|
||||
connectionToken,
|
||||
null,
|
||||
clientAddress,
|
||||
null))
|
||||
.getOrThrow();
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("New ClientConnection created: {}", clientConnection);
|
||||
// load client connection data into cache
|
||||
final ClientConnectionDataInternal activeClientConnection = this.examSessionCacheService
|
||||
.getActiveClientConnection(connectionToken);
|
||||
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Failed to load ClientConnectionDataInternal into cache on update");
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("New ClientConnection created: {}", clientConnection);
|
||||
}
|
||||
}
|
||||
|
||||
return clientConnection;
|
||||
|
@ -109,8 +118,8 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
public Result<ClientConnection> updateClientConnection(
|
||||
final String connectionToken,
|
||||
final Long institutionId,
|
||||
final String clientAddress,
|
||||
final Long examId,
|
||||
final String clientAddress,
|
||||
final String userSessionId) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
@ -131,9 +140,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
|
||||
checkExamRunning(examId);
|
||||
|
||||
final ClientConnection clientConnection = getClientConnection(
|
||||
connectionToken,
|
||||
institutionId);
|
||||
final ClientConnection clientConnection = getClientConnection(connectionToken);
|
||||
|
||||
checkInstitutionalIntegrity(
|
||||
institutionId,
|
||||
|
@ -156,7 +163,15 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
virtualClientAddress))
|
||||
.getOrThrow();
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
// evict cached ClientConnection
|
||||
this.examSessionCacheService.evictClientConnection(connectionToken);
|
||||
// and load updated ClientConnection into cache
|
||||
final ClientConnectionDataInternal activeClientConnection = this.examSessionCacheService
|
||||
.getActiveClientConnection(connectionToken);
|
||||
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Failed to load ClientConnectionDataInternal into cache on update");
|
||||
} else if (log.isDebugEnabled()) {
|
||||
log.debug("SEB client connection, successfully updated ClientConnection: {}",
|
||||
updatedClientConnection);
|
||||
}
|
||||
|
@ -192,9 +207,7 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
|
||||
checkExamRunning(examId);
|
||||
|
||||
final ClientConnection clientConnection = getClientConnection(
|
||||
connectionToken,
|
||||
institutionId);
|
||||
final ClientConnection clientConnection = getClientConnection(connectionToken);
|
||||
|
||||
checkInstitutionalIntegrity(
|
||||
institutionId,
|
||||
|
@ -217,42 +230,40 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
clientConnection.id,
|
||||
null,
|
||||
examId,
|
||||
ClientConnection.ConnectionStatus.ESTABLISHED,
|
||||
ConnectionStatus.ESTABLISHED,
|
||||
null,
|
||||
userSessionId,
|
||||
null,
|
||||
virtualClientAddress);
|
||||
|
||||
// ClientConnection integrity
|
||||
if (establishedClientConnection.institutionId == null ||
|
||||
if (clientConnection.institutionId == null ||
|
||||
clientConnection.connectionToken == null ||
|
||||
establishedClientConnection.examId == null ||
|
||||
establishedClientConnection.clientAddress == null ||
|
||||
establishedClientConnection.connectionToken == null) {
|
||||
clientConnection.clientAddress == null ||
|
||||
establishedClientConnection.status != ConnectionStatus.ESTABLISHED) {
|
||||
|
||||
log.error("ClientConnection integrity violation: {}", establishedClientConnection);
|
||||
throw new IllegalStateException("ClientConnection integrity violation: " + establishedClientConnection);
|
||||
log.error("ClientConnection integrity violation, clientConnection: {}, establishedClientConnection: {}",
|
||||
clientConnection,
|
||||
establishedClientConnection);
|
||||
throw new IllegalStateException("ClientConnection integrity violation");
|
||||
}
|
||||
|
||||
final ClientConnection updatedClientConnection = this.clientConnectionDAO
|
||||
.save(establishedClientConnection)
|
||||
.getOrThrow();
|
||||
|
||||
if (updatedClientConnection.status == ConnectionStatus.ESTABLISHED) {
|
||||
// load into cache...
|
||||
final ClientConnectionDataInternal activeClientConnection = this.examSessionCacheService
|
||||
.getActiveClientConnection(updatedClientConnection.connectionToken);
|
||||
// evict cached ClientConnection
|
||||
this.examSessionCacheService.evictClientConnection(connectionToken);
|
||||
// and load updated ClientConnection into cache
|
||||
final ClientConnectionDataInternal activeClientConnection = this.examSessionCacheService
|
||||
.getActiveClientConnection(connectionToken);
|
||||
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Unable to access and cache ClientConnection");
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("ClientConnection: {} successfully established", clientConnection);
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("ClientConnection: {} updated", clientConnection);
|
||||
}
|
||||
if (activeClientConnection == null) {
|
||||
log.warn("Failed to load ClientConnectionDataInternal into cache on update");
|
||||
} else if (log.isDebugEnabled()) {
|
||||
log.debug("SEB client connection, successfully established ClientConnection: {}",
|
||||
updatedClientConnection);
|
||||
}
|
||||
|
||||
return updatedClientConnection;
|
||||
|
@ -278,18 +289,14 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
}
|
||||
|
||||
final ClientConnection clientConnection = this.clientConnectionDAO
|
||||
.byConnectionToken(institutionId, connectionToken)
|
||||
.byConnectionToken(connectionToken)
|
||||
.getOrThrow();
|
||||
|
||||
// evict ClientConnection from cache
|
||||
this.examSessionCacheService
|
||||
.evictClientConnection(clientConnection.connectionToken);
|
||||
|
||||
final ClientConnection updatedClientConnection = this.clientConnectionDAO.save(new ClientConnection(
|
||||
clientConnection.id,
|
||||
null,
|
||||
null,
|
||||
ClientConnection.ConnectionStatus.CLOSED,
|
||||
ConnectionStatus.CLOSED,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
|
@ -300,6 +307,11 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
clientConnection);
|
||||
}
|
||||
|
||||
// evict cached ClientConnection
|
||||
this.examSessionCacheService.evictClientConnection(connectionToken);
|
||||
// and load updated ClientConnection into cache
|
||||
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||
|
||||
return updatedClientConnection;
|
||||
});
|
||||
|
||||
|
@ -326,12 +338,27 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
final String connectionToken,
|
||||
final ClientEvent event) {
|
||||
|
||||
this.eventHandlingStrategy.accept(event);
|
||||
|
||||
final ClientConnectionDataInternal activeClientConnection =
|
||||
this.examSessionCacheService.getActiveClientConnection(connectionToken);
|
||||
|
||||
if (activeClientConnection != null) {
|
||||
if (activeClientConnection.clientConnection.status != ConnectionStatus.ESTABLISHED) {
|
||||
throw new IllegalStateException("ClientConnection is not fully established or closed");
|
||||
}
|
||||
|
||||
// store event
|
||||
this.eventHandlingStrategy.accept(
|
||||
(event.connectionId != null)
|
||||
? event
|
||||
: new ClientEvent(
|
||||
null,
|
||||
activeClientConnection.getConnectionId(),
|
||||
event.eventType,
|
||||
event.timestamp,
|
||||
event.numValue,
|
||||
event.text));
|
||||
|
||||
// update indicators
|
||||
activeClientConnection.getindicatorMapping(event.eventType)
|
||||
.stream()
|
||||
.forEach(indicator -> indicator.notifyValueChange(event));
|
||||
|
@ -344,9 +371,9 @@ public class SebClientConnectionServiceImpl implements SebClientConnectionServic
|
|||
}
|
||||
}
|
||||
|
||||
private ClientConnection getClientConnection(final String connectionToken, final Long institutionId) {
|
||||
private ClientConnection getClientConnection(final String connectionToken) {
|
||||
final ClientConnection clientConnection = this.clientConnectionDAO
|
||||
.byConnectionToken(institutionId, connectionToken)
|
||||
.byConnectionToken(connectionToken)
|
||||
.getOrThrow();
|
||||
return clientConnection;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrate
|
|||
public class SingleEventSaveStrategy implements EventHandlingStrategy {
|
||||
|
||||
private final ClientEventDAO clientEventDAO;
|
||||
private boolean enabled = false;
|
||||
|
||||
public SingleEventSaveStrategy(final ClientEventDAO clientEventDAO) {
|
||||
this.clientEventDAO = clientEventDAO;
|
||||
|
@ -37,7 +38,24 @@ public class SingleEventSaveStrategy implements EventHandlingStrategy {
|
|||
|
||||
@Override
|
||||
public void accept(final ClientEvent event) {
|
||||
this.clientEventDAO.save(event);
|
||||
this.clientEventDAO
|
||||
.createNew(event)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
this.enabled = true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.http.MediaType;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -31,6 +32,8 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.api.POSTMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
|
@ -38,6 +41,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
|||
import ch.ethz.seb.sebserver.gbl.model.session.PingResponse;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.RunningExam;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.SebClientConfigDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||
|
@ -54,17 +58,20 @@ public class ExamAPI_V1_Controller {
|
|||
private final ExamSessionService examSessionService;
|
||||
private final SebClientConnectionService sebClientConnectionService;
|
||||
private final SebClientConfigDAO sebClientConfigDAO;
|
||||
private final JSONMapper jsonMapper;
|
||||
|
||||
protected ExamAPI_V1_Controller(
|
||||
final ExamDAO examDAO,
|
||||
final ExamSessionService examSessionService,
|
||||
final SebClientConnectionService sebClientConnectionService,
|
||||
final SebClientConfigDAO sebClientConfigDAO) {
|
||||
final SebClientConfigDAO sebClientConfigDAO,
|
||||
final JSONMapper jsonMapper) {
|
||||
|
||||
this.examDAO = examDAO;
|
||||
this.examSessionService = examSessionService;
|
||||
this.sebClientConnectionService = sebClientConnectionService;
|
||||
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||
this.jsonMapper = jsonMapper;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
|
@ -143,8 +150,8 @@ public class ExamAPI_V1_Controller {
|
|||
method = RequestMethod.PATCH,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public 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_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
@ -164,7 +171,7 @@ public class ExamAPI_V1_Controller {
|
|||
remoteAddr);
|
||||
}
|
||||
|
||||
this.sebClientConnectionService.establishClientConnection(
|
||||
this.sebClientConnectionService.updateClientConnection(
|
||||
connectionToken,
|
||||
institutionId,
|
||||
examId,
|
||||
|
@ -178,8 +185,8 @@ public class ExamAPI_V1_Controller {
|
|||
method = RequestMethod.PUT,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public 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_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestParam(name = API.EXAM_API_USER_SESSION_ID, required = false) final String userSessionId,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
@ -210,8 +217,8 @@ public class ExamAPI_V1_Controller {
|
|||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||
method = RequestMethod.DELETE,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public void handshakeEstablish(
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
public void handshakeDelete(
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
final Principal principal,
|
||||
final HttpServletRequest request) {
|
||||
|
||||
|
@ -239,25 +246,30 @@ public class ExamAPI_V1_Controller {
|
|||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public ResponseEntity<StreamingResponseBody> getConfig(
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
final Principal principal) {
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken) {
|
||||
|
||||
final Long institutionId = getInstitutionId(principal);
|
||||
final StreamingResponseBody stream = out -> this.examSessionService.streamDefaultExamConfig(
|
||||
institutionId,
|
||||
connectionToken,
|
||||
out);
|
||||
final StreamingResponseBody stream = out -> {
|
||||
try {
|
||||
this.examSessionService
|
||||
.streamDefaultExamConfig(
|
||||
connectionToken,
|
||||
out);
|
||||
} catch (final Exception e) {
|
||||
final APIMessage errorMessage = APIMessage.ErrorMessage.GENERIC.of(e.getMessage());
|
||||
out.write(Utils.toByteArray(this.jsonMapper.writeValueAsString(errorMessage)));
|
||||
}
|
||||
};
|
||||
|
||||
return new ResponseEntity<>(stream, HttpStatus.OK);
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.EXAM_API_PING_ENDPOINT,
|
||||
method = RequestMethod.PUT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public PingResponse ping(
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@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) {
|
||||
|
||||
|
@ -272,9 +284,9 @@ public class ExamAPI_V1_Controller {
|
|||
@RequestMapping(
|
||||
path = API.EXAM_API_EVENT_ENDPOINT,
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public void event(
|
||||
@RequestParam(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestHeader(name = API.EXAM_API_SEB_CONNECTION_TOKEN, required = true) final String connectionToken,
|
||||
@RequestBody(required = true) final ClientEvent event) {
|
||||
|
||||
this.sebClientConnectionService.notifyClientEvent(connectionToken, event);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* 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.integration.api.exam;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public class ExamAPIAccessTokenRequestTest extends ExamAPIIntegrationTester {
|
||||
|
||||
@Test
|
||||
public void testRequestAccessToken() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
}
|
||||
|
||||
}
|
|
@ -9,12 +9,15 @@
|
|||
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.post;
|
||||
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.status;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
@ -25,6 +28,9 @@ import org.springframework.boot.json.JacksonJsonParser;
|
|||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
|
||||
|
@ -32,7 +38,9 @@ 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.setup.MockMvcBuilders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
@ -40,6 +48,7 @@ import org.springframework.web.context.WebApplicationContext;
|
|||
|
||||
import ch.ethz.seb.sebserver.SEBServer;
|
||||
import ch.ethz.seb.sebserver.WebSecurityConfig;
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebClientConfigService;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.oauth.AdminAPIClientDetails;
|
||||
|
@ -66,6 +75,9 @@ public abstract class ExamAPIIntegrationTester {
|
|||
|
||||
protected MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
protected CacheManager cacheManager;
|
||||
|
||||
@MockBean
|
||||
public WebClientDetailsService webClientDetailsService;
|
||||
|
||||
|
@ -75,6 +87,12 @@ public abstract class ExamAPIIntegrationTester {
|
|||
.addFilter(this.springSecurityFilterChain).build();
|
||||
Mockito.when(this.webClientDetailsService.loadClientByClientId(Mockito.anyString())).thenReturn(
|
||||
getForExamClientAPI());
|
||||
|
||||
// clear all caches before a test
|
||||
this.cacheManager.getCacheNames()
|
||||
.stream()
|
||||
.map(name -> this.cacheManager.getCache(name))
|
||||
.forEach(cache -> cache.clear());
|
||||
}
|
||||
|
||||
protected ClientDetails getForExamClientAPI() {
|
||||
|
@ -103,9 +121,9 @@ public abstract class ExamAPIIntegrationTester {
|
|||
final ResultActions result = this.mockMvc.perform(post("/oauth/token")
|
||||
.params(params)
|
||||
.with(httpBasic(clientId, clientSecret))
|
||||
.accept("application/json;charset=UTF-8"))
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType("application/json;charset=UTF-8"));
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
|
||||
|
||||
final String resultString = result.andReturn().getResponse().getContentAsString();
|
||||
|
||||
|
@ -113,6 +131,152 @@ public abstract class ExamAPIIntegrationTester {
|
|||
return jsonParser.parseMap(resultString).get("access_token").toString();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse createConnection(
|
||||
final String accessToken,
|
||||
final Long institutionId,
|
||||
final Long examId) throws Exception {
|
||||
|
||||
final MockHttpServletRequestBuilder builder = get(this.endpoint + "/handshake")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
|
||||
String body = "";
|
||||
|
||||
if (institutionId != null) {
|
||||
body += "institutionId=" + institutionId;
|
||||
}
|
||||
if (examId != null) {
|
||||
body += "&examId=" + examId;
|
||||
}
|
||||
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder)
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse updateConnection(
|
||||
final String accessToken,
|
||||
final String connectionToken,
|
||||
final Long examId,
|
||||
final String userSessionId) throws Exception {
|
||||
|
||||
return updateConnection(accessToken, connectionToken, examId, userSessionId, false);
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse establishConnection(
|
||||
final String accessToken,
|
||||
final String connectionToken,
|
||||
final Long examId,
|
||||
final String userSessionId) throws Exception {
|
||||
|
||||
return updateConnection(accessToken, connectionToken, examId, userSessionId, true);
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse updateConnection(
|
||||
final String accessToken,
|
||||
final String connectionToken,
|
||||
final Long examId,
|
||||
final String userSessionId,
|
||||
final boolean establish) throws Exception {
|
||||
|
||||
final MockHttpServletRequestBuilder builder = (establish)
|
||||
? put(this.endpoint + "/handshake")
|
||||
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
: patch(this.endpoint + "/handshake")
|
||||
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
|
||||
String body = "";
|
||||
if (examId != null) {
|
||||
body += "examId=" + examId;
|
||||
}
|
||||
if (userSessionId != null) {
|
||||
if (!StringUtils.isBlank(body)) {
|
||||
body += "&";
|
||||
}
|
||||
|
||||
body += API.EXAM_API_USER_SESSION_ID + "=" + userSessionId;
|
||||
}
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse closeConnection(final String accessToken, final String connectionToken)
|
||||
throws Exception {
|
||||
final MockHttpServletRequestBuilder builder = delete(this.endpoint + "/handshake")
|
||||
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.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);
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse sendPing(
|
||||
final String accessToken,
|
||||
final String connectionToken,
|
||||
final int num) throws Exception {
|
||||
final MockHttpServletRequestBuilder builder = post(this.endpoint + API.EXAM_API_PING_ENDPOINT)
|
||||
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
|
||||
final String body = API.EXAM_API_PING_TIMESTAMP + "=" + DateTime.now(DateTimeZone.UTC).getMillis()
|
||||
+ "&" + API.EXAM_API_PING_NUMBER + "=" + num;
|
||||
builder.content(body);
|
||||
|
||||
final ResultActions result = this.mockMvc.perform(builder);
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse sendEvent(
|
||||
final String accessToken,
|
||||
final String connectionToken,
|
||||
final String type,
|
||||
final long timestamp,
|
||||
final double value,
|
||||
final String text) throws Exception {
|
||||
|
||||
final MockHttpServletRequestBuilder builder = post(this.endpoint + API.EXAM_API_EVENT_ENDPOINT)
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
|
||||
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);
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
protected MockHttpServletResponse getExamConfig(final String accessToken, final String connectionToken)
|
||||
throws Exception {
|
||||
final MockHttpServletRequestBuilder builder = get(this.endpoint + API.EXAM_API_CONFIGURATION_REQUEST_ENDPOINT)
|
||||
.header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.header(API.EXAM_API_SEB_CONNECTION_TOKEN, connectionToken)
|
||||
.accept(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||
|
||||
final ResultActions result = this.mockMvc
|
||||
.perform(builder)
|
||||
.andDo(MvcResult::getAsyncResult);
|
||||
|
||||
return result.andReturn().getResponse();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
AdminAPIClientDetails adminClientDetails;
|
||||
@Autowired
|
||||
|
|
|
@ -0,0 +1,624 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* 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.integration.api.exam;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.Cache.ValueWrapper;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientEventRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.AbstractPingIndicator;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ClientConnectionDataInternal;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.ExamSessionCacheService;
|
||||
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public class SebConnectionTest extends ExamAPIIntegrationTester {
|
||||
|
||||
@Autowired
|
||||
private ClientConnectionRecordMapper clientConnectionRecordMapper;
|
||||
@Autowired
|
||||
private ClientEventRecordMapper clientEventRecordMapper;
|
||||
@Autowired
|
||||
private JSONMapper jsonMapper;
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testCreateConnection() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == createConnection.getStatus());
|
||||
final String contentAsString = createConnection.getContentAsString();
|
||||
assertEquals("[{\"examId\":\"2\",\"name\":\"Demo Quiz 6\",\"url\":\"http://lms.mockup.com/api/\"}]",
|
||||
contentAsString);
|
||||
|
||||
// check connection token
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
// check correct stored
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertNull(clientConnectionRecord.getExamId());
|
||||
assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertEquals(connectionToken, clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertNull(clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
|
||||
// check caching
|
||||
final Cache examCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_RUNNING_EXAM);
|
||||
final ValueWrapper exam = examCache.get(2L);
|
||||
assertNotNull(exam);
|
||||
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
final ValueWrapper connection = connectionCache.get(connectionToken);
|
||||
assertNotNull(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testCreateConnectionWithExamId() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, 2L);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == createConnection.getStatus());
|
||||
final String contentAsString = createConnection.getContentAsString();
|
||||
assertEquals("[{\"examId\":\"2\",\"name\":\"Demo Quiz 6\",\"url\":\"http://lms.mockup.com/api/\"}]",
|
||||
contentAsString);
|
||||
|
||||
// check connection token
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
// check correct stored
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertEquals("2", String.valueOf(clientConnectionRecord.getExamId()));
|
||||
assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertEquals(connectionToken, clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertNull(clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testCreateConnectionWithWrongExamId() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, 1L);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
// expecting error status
|
||||
assertTrue(createConnection.getStatus() != HttpStatus.OK.value());
|
||||
final String contentAsString = createConnection.getContentAsString();
|
||||
final Collection<APIMessage> errorMessage = this.jsonMapper.readValue(
|
||||
contentAsString,
|
||||
new TypeReference<Collection<APIMessage>>() {
|
||||
});
|
||||
final APIMessage error = errorMessage.iterator().next();
|
||||
assertEquals(ErrorMessage.UNEXPECTED.messageCode, error.messageCode);
|
||||
assertEquals("The exam 1 is not running", error.details);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testCreateConnectionNoInstitutionId() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, null, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
// expecting error status
|
||||
assertTrue(createConnection.getStatus() != HttpStatus.OK.value());
|
||||
final String contentAsString = createConnection.getContentAsString();
|
||||
final Collection<APIMessage> errorMessage = this.jsonMapper.readValue(
|
||||
contentAsString,
|
||||
new TypeReference<Collection<APIMessage>>() {
|
||||
});
|
||||
final APIMessage error = errorMessage.iterator().next();
|
||||
assertEquals(ErrorMessage.GENERIC.messageCode, error.messageCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testUpdateConnection() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
// check cache after creation
|
||||
Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNull(ccdi.clientConnection.examId);
|
||||
assertTrue(ccdi.indicatorValues.isEmpty());
|
||||
|
||||
final MockHttpServletResponse updatedConnection = super.updateConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
2L,
|
||||
"userSessionId");
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == updatedConnection.getStatus());
|
||||
|
||||
// check correct stored
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertEquals("2", String.valueOf(clientConnectionRecord.getExamId()));
|
||||
assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertNotNull(clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertEquals("userSessionId", clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
|
||||
// check cache after update
|
||||
connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testUpdateConnectionWithWrongConnectionToken() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse updatedConnection = super.updateConnection(
|
||||
accessToken,
|
||||
"",
|
||||
2L,
|
||||
"userSessionId");
|
||||
|
||||
// expecting error status
|
||||
assertTrue(HttpStatus.OK.value() != updatedConnection.getStatus());
|
||||
final String contentAsString = updatedConnection.getContentAsString();
|
||||
final Collection<APIMessage> errorMessage = this.jsonMapper.readValue(
|
||||
contentAsString,
|
||||
new TypeReference<Collection<APIMessage>>() {
|
||||
});
|
||||
final APIMessage error = errorMessage.iterator().next();
|
||||
assertEquals(ErrorMessage.RESOURCE_NOT_FOUND.messageCode, error.messageCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testEstablishConnection() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
// check cache after creation
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNull(ccdi.clientConnection.examId);
|
||||
assertTrue(ccdi.indicatorValues.isEmpty());
|
||||
|
||||
final MockHttpServletResponse updatedConnection = super.establishConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
2L,
|
||||
"userSessionId");
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == updatedConnection.getStatus());
|
||||
|
||||
// check correct stored
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertEquals("2", String.valueOf(clientConnectionRecord.getExamId()));
|
||||
assertEquals("ESTABLISHED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertNotNull(clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertEquals("userSessionId", clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
|
||||
// check cache after update
|
||||
ccdi = (ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testEstablishConnectionNoExamLeadsToError() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
// check cache after creation
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNull(ccdi.clientConnection.examId);
|
||||
assertTrue(ccdi.indicatorValues.isEmpty());
|
||||
|
||||
final MockHttpServletResponse updatedConnection = super.establishConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
null,
|
||||
null);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() != updatedConnection.getStatus());
|
||||
final String contentAsString = updatedConnection.getContentAsString();
|
||||
final Collection<APIMessage> errorMessage = this.jsonMapper.readValue(
|
||||
contentAsString,
|
||||
new TypeReference<Collection<APIMessage>>() {
|
||||
});
|
||||
final APIMessage error = errorMessage.iterator().next();
|
||||
assertEquals(ErrorMessage.UNEXPECTED.messageCode, error.messageCode);
|
||||
assertEquals("ClientConnection integrity violation", error.details);
|
||||
|
||||
// check correct stored (no changes)
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertNull(clientConnectionRecord.getExamId());
|
||||
assertEquals("CONNECTION_REQUESTED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertNotNull(clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertNull(clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
|
||||
// check cache fail remains the same
|
||||
ccdi = (ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNull(ccdi.clientConnection.examId);
|
||||
assertTrue(ccdi.indicatorValues.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testCloseConnection() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
final MockHttpServletResponse establishConnection = super.establishConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
2L,
|
||||
null);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == establishConnection.getStatus());
|
||||
|
||||
// check cache after creation
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
|
||||
// close connection
|
||||
final MockHttpServletResponse closedConnection = super.closeConnection(
|
||||
accessToken,
|
||||
connectionToken);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == closedConnection.getStatus());
|
||||
|
||||
// check correct stored (no changes)
|
||||
final List<ClientConnectionRecord> records = this.clientConnectionRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertTrue(records.size() == 1);
|
||||
final ClientConnectionRecord clientConnectionRecord = records.get(0);
|
||||
assertEquals("1", String.valueOf(clientConnectionRecord.getInstitutionId()));
|
||||
assertEquals("2", String.valueOf(clientConnectionRecord.getExamId()));
|
||||
assertEquals("CLOSED", String.valueOf(clientConnectionRecord.getStatus()));
|
||||
assertNotNull(clientConnectionRecord.getConnectionToken());
|
||||
assertNotNull(clientConnectionRecord.getClientAddress());
|
||||
assertNull(clientConnectionRecord.getExamUserSessionIdentifer());
|
||||
assertNull(clientConnectionRecord.getVirtualClientAddress());
|
||||
|
||||
// check cache after update
|
||||
ccdi = (ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
assertEquals("CLOSED", ccdi.clientConnection.status.toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testPing() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
final MockHttpServletResponse establishConnection = super.establishConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
2L,
|
||||
null);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == establishConnection.getStatus());
|
||||
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
final ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next();
|
||||
assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING);
|
||||
assertEquals("0", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
|
||||
super.sendPing(accessToken, connectionToken, 1);
|
||||
assertEquals("1", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
super.sendPing(accessToken, connectionToken, 2);
|
||||
assertEquals("2", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
super.sendPing(accessToken, connectionToken, 3);
|
||||
assertEquals("3", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
super.sendPing(accessToken, connectionToken, 5);
|
||||
assertEquals("5", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testSendPingToNoneEstablishedConnectionIsIgnored() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
final MockHttpServletResponse sendPing = super.sendPing(accessToken, connectionToken, 1);
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == sendPing.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testEvent() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
final MockHttpServletResponse establishConnection = super.establishConnection(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
2L,
|
||||
null);
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == establishConnection.getStatus());
|
||||
|
||||
final Cache connectionCache = this.cacheManager
|
||||
.getCache(ExamSessionCacheService.CACHE_NAME_ACTIVE_CLIENT_CONNECTION);
|
||||
final ClientConnectionDataInternal ccdi =
|
||||
(ClientConnectionDataInternal) connectionCache.get(connectionToken).get();
|
||||
assertNotNull(ccdi);
|
||||
assertNotNull(ccdi.clientConnection.examId);
|
||||
assertFalse(ccdi.indicatorValues.isEmpty());
|
||||
final IndicatorValue pingIndicator = ccdi.indicatorValues.iterator().next();
|
||||
assertTrue(pingIndicator.getType() == IndicatorType.LAST_PING);
|
||||
assertEquals("0", String.valueOf(((AbstractPingIndicator) pingIndicator).getPingNumber()));
|
||||
|
||||
MockHttpServletResponse sendEvent = super.sendEvent(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
"INFO_LOG",
|
||||
1l,
|
||||
100.0,
|
||||
"testEvent1");
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == sendEvent.getStatus());
|
||||
|
||||
// check event stored on db
|
||||
List<ClientEventRecord> events = this.clientEventRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertFalse(events.isEmpty());
|
||||
final ClientEventRecord clientEventRecord = events.get(0);
|
||||
assertEquals(
|
||||
"ClientEventRecord ["
|
||||
+ "Hash = -1088444763, "
|
||||
+ "id=1, "
|
||||
+ "connectionId=1, "
|
||||
+ "type=2, "
|
||||
+ "timestamp=1, "
|
||||
+ "numericValue=100.0000, "
|
||||
+ "text=testEvent1]",
|
||||
clientEventRecord.toString());
|
||||
|
||||
// send another event
|
||||
sendEvent = super.sendEvent(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
"ERROR_LOG",
|
||||
2l,
|
||||
10000.0,
|
||||
"testEvent2");
|
||||
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == sendEvent.getStatus());
|
||||
|
||||
// check event stored on db
|
||||
events = this.clientEventRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
|
||||
assertFalse(events.isEmpty());
|
||||
assertTrue(events.stream().filter(ev -> ev.getTimestamp().equals(2l)).findFirst().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testSendEventToNoneEstablishedConnectionLeadsToError() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse createConnection = super.createConnection(accessToken, 1L, null);
|
||||
assertNotNull(createConnection);
|
||||
|
||||
final String connectionToken = createConnection.getHeader(API.EXAM_API_SEB_CONNECTION_TOKEN);
|
||||
assertNotNull(connectionToken);
|
||||
|
||||
final MockHttpServletResponse sendEvent = super.sendEvent(
|
||||
accessToken,
|
||||
connectionToken,
|
||||
"INFO_LOG",
|
||||
1l,
|
||||
100.0,
|
||||
"testEvent1");
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() != sendEvent.getStatus());
|
||||
|
||||
final List<ClientEventRecord> events = this.clientEventRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
assertTrue(events.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Sql(scripts = { "classpath:schema-test.sql", "classpath:data-test.sql", "classpath:data-test-additional.sql" })
|
||||
public void testSendEventToNoneExistingConnectionIsIgnored() throws Exception {
|
||||
final String accessToken = super.obtainAccessToken("test", "test", "SEBClient");
|
||||
assertNotNull(accessToken);
|
||||
|
||||
final MockHttpServletResponse sendEvent = super.sendEvent(
|
||||
accessToken,
|
||||
"someInvalidConnectionToken",
|
||||
"INFO_LOG",
|
||||
1l,
|
||||
100.0,
|
||||
"testEvent1");
|
||||
// check correct response
|
||||
assertTrue(HttpStatus.OK.value() == sendEvent.getStatus());
|
||||
|
||||
final List<ClientEventRecord> events = this.clientEventRecordMapper
|
||||
.selectByExample()
|
||||
.build()
|
||||
.execute();
|
||||
assertTrue(events.isEmpty());
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -21,7 +21,6 @@ sebserver.webservice.api.exam.endpoint.discovery=${sebserver.webservice.api.exam
|
|||
sebserver.webservice.api.exam.endpoint.v1=${sebserver.webservice.api.exam.endpoint}/v1
|
||||
sebserver.webservice.api.exam.accessTokenValiditySeconds=1800
|
||||
sebserver.webservice.api.exam.refreshTokenValiditySeconds=-1
|
||||
sebserver.webservice.internalSecret=TO_SET
|
||||
sebserver.webservice.api.redirect.unauthorized=none
|
||||
# comma separated list of known possible OpenEdX API access token request endpoints
|
||||
sebserver.webservice.lms.openedx.api.token.request.paths=/oauth2/access_token
|
||||
|
|
Loading…
Reference in a new issue