Merge remote-tracking branch 'origin/dev-1.4' into development
This commit is contained in:
commit
40689d5781
10 changed files with 68 additions and 41 deletions
2
pom.xml
2
pom.xml
|
@ -18,7 +18,7 @@
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<sebserver-version>1.4-rc1</sebserver-version>
|
<sebserver-version>1.4.0-SNAPSHOT</sebserver-version>
|
||||||
<build-version>${sebserver-version}</build-version>
|
<build-version>${sebserver-version}</build-version>
|
||||||
<revision>${sebserver-version}</revision>
|
<revision>${sebserver-version}</revision>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gbl.model.session;
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ public final class RunningExamInfo {
|
||||||
this.examId = exam.getModelId();
|
this.examId = exam.getModelId();
|
||||||
this.name = exam.name;
|
this.name = exam.name;
|
||||||
this.url = exam.getStartURL();
|
this.url = exam.getStartURL();
|
||||||
this.lmsType = (lmsType == null) ? "" : lmsType.name();
|
this.lmsType = (lmsType == null) ? Constants.EMPTY_NOTE : lmsType.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExamId() {
|
public String getExamId() {
|
||||||
|
|
|
@ -340,7 +340,7 @@ public class ConfigTemplateForm implements TemplateComposer {
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.publishIf(() -> modifyGrant && isReadonly)
|
.publishIf(() -> modifyGrant && isReadonly)
|
||||||
|
|
||||||
.newAction(ActionDefinition.SEB_EXAM_CONFIG_DELETE)
|
.newAction(ActionDefinition.SEB_EXAM_CONFIG_TEMPLATE_DELETE)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withConfirm(() -> CONFIRM_DELETE)
|
.withConfirm(() -> CONFIRM_DELETE)
|
||||||
.withExec(this::deleteConfiguration)
|
.withExec(this::deleteConfiguration)
|
||||||
|
|
|
@ -648,6 +648,9 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
.and(
|
.and(
|
||||||
ExamRecordDynamicSqlSupport.status,
|
ExamRecordDynamicSqlSupport.status,
|
||||||
isEqualTo(ExamStatus.RUNNING.name()))
|
isEqualTo(ExamStatus.RUNNING.name()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.lmsAvailable,
|
||||||
|
isEqualToWhenPresent(BooleanUtils.toIntegerObject(true)))
|
||||||
.build()
|
.build()
|
||||||
.execute());
|
.execute());
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ public interface ExamSessionService {
|
||||||
/** Use this to check if a specified Exam has currently active SEB Client connections.
|
/** Use this to check if a specified Exam has currently active SEB Client connections.
|
||||||
*
|
*
|
||||||
* Active SEB Client connections are established connections that are not yet closed and
|
* Active SEB Client connections are established connections that are not yet closed and
|
||||||
* connection attempts that are older the a defined time interval.
|
* open connection attempts.
|
||||||
*
|
*
|
||||||
* @param examId The Exam identifier
|
* @param examId The Exam identifier
|
||||||
* @return true if the given Exam has currently no active client connection, false otherwise. */
|
* @return true if the given Exam has currently no active client connection, false otherwise. */
|
||||||
|
@ -86,7 +86,7 @@ public interface ExamSessionService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !this.getActiveConnectionTokens(examId)
|
return !this.getAllActiveConnectionTokens(examId)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -184,13 +184,21 @@ public interface ExamSessionService {
|
||||||
final Long examId,
|
final Long examId,
|
||||||
final Predicate<ClientConnectionData> filter);
|
final Predicate<ClientConnectionData> filter);
|
||||||
|
|
||||||
/** Gets all connection tokens of active client connection that are related to a specified exam
|
/** Gets all connection tokens of client connection that are in ACTIVE state and related to a specified exam
|
||||||
* from persistence storage without caching involved.
|
* from persistence storage without caching involved.
|
||||||
*
|
*
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @return Result refer to the collection of connection tokens or to an error when happened. */
|
* @return Result refer to the collection of connection tokens or to an error when happened. */
|
||||||
Result<Collection<String>> getActiveConnectionTokens(Long examId);
|
Result<Collection<String>> getActiveConnectionTokens(Long examId);
|
||||||
|
|
||||||
|
/** Gets all connection tokens of client connections that are in an active state. See <code>ClientConnection</code>
|
||||||
|
* And that are related to a specified exam.
|
||||||
|
* There is no caching involved here, gets actual data from persistent storage
|
||||||
|
*
|
||||||
|
* @param examId the exam identifier
|
||||||
|
* @return Result refer to the collection of connection tokens or to an error when happened. */
|
||||||
|
Result<Collection<String>> getAllActiveConnectionTokens(Long examId);
|
||||||
|
|
||||||
/** Use this to check if the current cached running exam is up to date
|
/** Use this to check if the current cached running exam is up to date
|
||||||
* and if not to flush the cache.
|
* and if not to flush the cache.
|
||||||
*
|
*
|
||||||
|
|
|
@ -430,6 +430,12 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
.getActiveConnectionTokens(examId);
|
.getActiveConnectionTokens(examId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Collection<String>> getAllActiveConnectionTokens(final Long examId) {
|
||||||
|
return this.clientConnectionDAO
|
||||||
|
.getAllActiveConnectionTokens(examId);
|
||||||
|
}
|
||||||
|
|
||||||
@EventListener
|
@EventListener
|
||||||
public void notifyExamFinished(final ExamFinishedEvent event) {
|
public void notifyExamFinished(final ExamFinishedEvent event) {
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ public class ExamAPI_V1_Controller {
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::createRunningExamInfo)
|
.map(this::createRunningExamInfo)
|
||||||
|
.filter(this::checkConsistency)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
} else {
|
} else {
|
||||||
final Exam exam = this.examSessionService.getExamDAO()
|
final Exam exam = this.examSessionService.getExamDAO()
|
||||||
|
@ -158,6 +159,18 @@ public class ExamAPI_V1_Controller {
|
||||||
this.executor);
|
this.executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkConsistency(final RunningExamInfo info) {
|
||||||
|
if (StringUtils.isNotBlank(info.name) &&
|
||||||
|
StringUtils.isNotBlank(info.url) &&
|
||||||
|
StringUtils.isNotBlank(info.examId)) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("Invalid running exam detected. Filter out exam : {}", info);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
path = API.EXAM_API_HANDSHAKE_ENDPOINT,
|
||||||
method = RequestMethod.PATCH,
|
method = RequestMethod.PATCH,
|
||||||
|
|
|
@ -568,7 +568,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
||||||
.of("Exam currently has active SEB Client connections."));
|
.of("Exam currently has active SEB Client connections."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO double check before setSEBRestriction
|
|
||||||
return this.checkNoActiveSEBClientConnections(exam)
|
return this.checkNoActiveSEBClientConnections(exam)
|
||||||
.flatMap(this.sebRestrictionService::applySEBClientRestriction)
|
.flatMap(this.sebRestrictionService::applySEBClientRestriction)
|
||||||
.flatMap(e -> this.examDAO.setSEBRestriction(exam.id, restrict))
|
.flatMap(e -> this.examDAO.setSEBRestriction(exam.id, restrict))
|
||||||
|
|
|
@ -95,6 +95,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.SEBClientConfig;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
|
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
||||||
|
@ -254,35 +255,6 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Test
|
|
||||||
// @Order(0)
|
|
||||||
// public void testUsecase00_cleanupAllExams() {
|
|
||||||
// final RestServiceImpl restService = createRestServiceForUser(
|
|
||||||
// "admin",
|
|
||||||
// "admin",
|
|
||||||
// new GetExamNames(),
|
|
||||||
// new DeleteExam());
|
|
||||||
//
|
|
||||||
// final Result<List<EntityName>> call = restService
|
|
||||||
// .getBuilder(GetExamNames.class)
|
|
||||||
// .call();
|
|
||||||
//
|
|
||||||
// if (!call.hasError()) {
|
|
||||||
// call.get().stream().forEach(key -> {
|
|
||||||
// final Result<EntityProcessingReport> deleted = restService
|
|
||||||
// .getBuilder(DeleteExam.class)
|
|
||||||
// .withURIVariable(API.PARAM_MODEL_ID, key.modelId)
|
|
||||||
// .call();
|
|
||||||
//
|
|
||||||
// if (deleted.hasError()) {
|
|
||||||
// System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%% deletion failed: " + key);
|
|
||||||
// } else {
|
|
||||||
// System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%% deleted: " + key);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
// *************************************
|
// *************************************
|
||||||
|
@ -815,6 +787,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
// - Check if there are some quizzes from previous LMS Setup
|
// - Check if there are some quizzes from previous LMS Setup
|
||||||
// - Import a quiz as Exam
|
// - Import a quiz as Exam
|
||||||
// - get exam page and check the exam is there
|
// - get exam page and check the exam is there
|
||||||
|
// - get exam page with none native sort attribute to test this
|
||||||
// - edit exam property and save again
|
// - edit exam property and save again
|
||||||
public void testUsecase07_ImportExam() {
|
public void testUsecase07_ImportExam() {
|
||||||
final RestServiceImpl restService = createRestServiceForUser(
|
final RestServiceImpl restService = createRestServiceForUser(
|
||||||
|
@ -922,6 +895,13 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
.filter(exam -> exam.name.equals(newExam.name))
|
.filter(exam -> exam.name.equals(newExam.name))
|
||||||
.findFirst().isPresent());
|
.findFirst().isPresent());
|
||||||
|
|
||||||
|
final Result<Page<Exam>> examsSorted = restService
|
||||||
|
.getBuilder(GetExamPage.class)
|
||||||
|
.withQueryParam(Page.ATTR_SORT, LmsSetup.FILTER_ATTR_LMS_SETUP)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
assertNotNull(examsSorted);
|
||||||
|
assertFalse(examsSorted.hasError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2169,7 +2149,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
assertTrue(connections.isEmpty());
|
assertTrue(connections.isEmpty());
|
||||||
|
|
||||||
// get MonitoringFullPageData
|
// get MonitoringFullPageData
|
||||||
final Result<MonitoringFullPageData> fullPageData = restService.getBuilder(GetMonitoringFullPageData.class)
|
Result<MonitoringFullPageData> fullPageData = restService.getBuilder(GetMonitoringFullPageData.class)
|
||||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId())
|
.withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId())
|
||||||
.call();
|
.call();
|
||||||
assertNotNull(fullPageData);
|
assertNotNull(fullPageData);
|
||||||
|
@ -2221,6 +2201,22 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
||||||
iterator.next();
|
iterator.next();
|
||||||
final ClientConnectionData con = iterator.next();
|
final ClientConnectionData con = iterator.next();
|
||||||
|
|
||||||
|
fullPageData = restService.getBuilder(GetMonitoringFullPageData.class)
|
||||||
|
.withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId())
|
||||||
|
.withHeader(API.EXAM_MONITORING_STATE_FILTER, ConnectionStatus.DISABLED.name())
|
||||||
|
.call();
|
||||||
|
assertNotNull(fullPageData);
|
||||||
|
assertFalse(fullPageData.hasError());
|
||||||
|
|
||||||
|
fullPageData = restService.getBuilder(GetMonitoringFullPageData.class)
|
||||||
|
.withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId())
|
||||||
|
.withHeader(
|
||||||
|
API.EXAM_MONITORING_STATE_FILTER,
|
||||||
|
ConnectionStatus.DISABLED.name() + "," + ConnectionStatus.ACTIVE.name())
|
||||||
|
.call();
|
||||||
|
assertNotNull(fullPageData);
|
||||||
|
assertFalse(fullPageData.hasError());
|
||||||
|
|
||||||
// get single client connection
|
// get single client connection
|
||||||
final Result<ClientConnection> ccCall = restService.getBuilder(GetClientConnection.class)
|
final Result<ClientConnection> ccCall = restService.getBuilder(GetClientConnection.class)
|
||||||
.withURIVariable(API.PARAM_MODEL_ID, con.clientConnection.getModelId())
|
.withURIVariable(API.PARAM_MODEL_ID, con.clientConnection.getModelId())
|
||||||
|
|
|
@ -9,13 +9,14 @@ INSERT IGNORE INTO seb_client_configuration VALUES
|
||||||
|
|
||||||
INSERT IGNORE INTO additional_attributes VALUES
|
INSERT IGNORE INTO additional_attributes VALUES
|
||||||
(1, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiSetup', 'VM_WARE'),
|
(1, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiSetup', 'VM_WARE'),
|
||||||
(2, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiExecutable', 'vmware-view.exe')
|
(2, 'SEB_CLIENT_CONFIGURATION', 2, 'vdiExecutable', 'vmware-view.exe'),
|
||||||
|
(3, 'EXAM', 2, 'quiz_start_url', 'https://test.lms.mockup')
|
||||||
;
|
;
|
||||||
|
|
||||||
INSERT IGNORE INTO exam VALUES
|
INSERT IGNORE INTO exam VALUES
|
||||||
(1, 1, 1, 'quiz1', 'admin', 'admin', 'MANAGED', null, null, 'UP_COMING', 1, 0, null, 1, null, null, null, null, null, null),
|
(1, 1, 1, 'quiz1', 'admin', 'admin', 'MANAGED', null, null, 'UP_COMING', 1, 0, null, 1, null, null, 'quiz1', null, null, 1),
|
||||||
(2, 1, 1, 'quiz6', 'admin', 'admin', 'MANAGED', null, null, 'RUNNING', 1, 0, null, 1, null, null, null, null, null, null),
|
(2, 1, 1, 'quiz6', 'admin', 'admin', 'MANAGED', null, null, 'RUNNING', 1, 0, null, 1, null, null, 'quiz6', null, null, 1),
|
||||||
(3, 1, 1, 'quiz3', 'admin', 'admin', 'MANAGED', null, null, 'FINISHED', 1, 0, null, 1, null, null, null, null, null, null)
|
(3, 1, 1, 'quiz3', 'admin', 'admin', 'MANAGED', null, null, 'FINISHED', 1, 0, null, 1, null, null, 'quiz3', null, null, 1)
|
||||||
;
|
;
|
||||||
|
|
||||||
INSERT IGNORE INTO indicator VALUES
|
INSERT IGNORE INTO indicator VALUES
|
||||||
|
|
Loading…
Reference in a new issue