From ee471e5b06ac71956037778aa87de742d3e6815d Mon Sep 17 00:00:00 2001 From: anhefti Date: Thu, 12 Dec 2024 12:33:17 +0100 Subject: [PATCH] SEBSP-116 try to recover when exam and groups do not exist anymore on SPS --- .../dao/ScreenProctoringGroupDAO.java | 9 +-- .../impl/ScreenProctoringGroupDAOImpl.java | 34 ++++---- .../session/impl/proctoring/SPS_API.java | 2 +- .../ScreenProctoringAPIBinding.java | 78 +++++++++++++++---- .../ScreenProctoringServiceImpl.java | 8 +- 5 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ScreenProctoringGroupDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ScreenProctoringGroupDAO.java index 3899737fa..9e1b3b0ba 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ScreenProctoringGroupDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ScreenProctoringGroupDAO.java @@ -34,13 +34,6 @@ public interface ScreenProctoringGroupDAO { * @return Result refer to the group or to an error when happened */ Result getScreenProctoringGroup(String uuid); - /** Get the group record with specified name for a given exam. - * - * @param examId the exam identifier - * @param groupName the name of the group - * @return Result refer to the group record or to an error when happened */ - Result getGroupByName(Long examId, String groupName); - /** Get all collecting group that exists for a given exam. * * @param examId the exam identifier @@ -85,4 +78,6 @@ public interface ScreenProctoringGroupDAO { boolean hasActiveGroups(); void updateName(Long id, String name); + + Result updateFromSPS(Long id, ScreenProctoringGroup groupOnSPS); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ScreenProctoringGroupDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ScreenProctoringGroupDAOImpl.java index 222177e34..be22ebc8a 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ScreenProctoringGroupDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ScreenProctoringGroupDAOImpl.java @@ -97,20 +97,6 @@ public Result getScreenProctoringGroup(final String uuid) .map(this::toDomainModel); } - @Override - @Transactional(readOnly = true) - public Result getGroupByName(final Long examId, final String groupName) { - return Result.tryCatch(() -> { - return this.screenProctoringGroopRecordMapper.selectByExample() - .where(ScreenProctoringGroopRecordDynamicSqlSupport.examId, isEqualTo(examId)) - .and(ScreenProctoringGroopRecordDynamicSqlSupport.name, isEqualTo(groupName)) - .build() - .execute() - .get(0); - }) - .map(this::toDomainModel); - } - @Override @Transactional(readOnly = true) public Result> getCollectingGroups(final Long examId) { @@ -318,6 +304,26 @@ public void updateName(final Long groupId, final String name) { } } + @Override + @Transactional + public Result updateFromSPS(final Long id, final ScreenProctoringGroup groupOnSPS) { + return Result.tryCatch(() -> { + UpdateDSL + .updateWithMapper( + this.screenProctoringGroopRecordMapper::update, + ScreenProctoringGroopRecordDynamicSqlSupport.screenProctoringGroopRecord) + .set(ScreenProctoringGroopRecordDynamicSqlSupport.uuid).equalTo(groupOnSPS.uuid) + .set(ScreenProctoringGroopRecordDynamicSqlSupport.data).equalTo(groupOnSPS.additionalData) + .where(ScreenProctoringGroopRecordDynamicSqlSupport.id, isEqualTo(id)) + .build() + .execute(); + + return id; + }) + .flatMap(this::getScreenProctoringGroup) + .onError(TransactionHandler::rollback); + } + private ScreenProctoringGroup toDomainModel(final ScreenProctoringGroopRecord record) { return new ScreenProctoringGroup( record.getId(), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/SPS_API.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/SPS_API.java index d01997dd8..2b01b2a97 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/SPS_API.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/SPS_API.java @@ -272,7 +272,7 @@ ResponseEntity testServiceConnection() { if (errors.contains("Connection refused")) { return new ResponseEntity<>( errors, HttpStatus.SERVICE_UNAVAILABLE); } - // TODO Test SSL error + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java index b90c9764a..e101b888b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringAPIBinding.java @@ -201,6 +201,13 @@ Result startScreenProctoring(final Exam exam) { log.info("SPS Exam for SEB Server Exam: {} already exists. Try to re-activate", exam.externalId); final SPSData spsData = this.getSPSData(exam.id); + + if (!existsExamOnSPS(exam)) { + log.warn("Exam does not exist on SPS but has local data. Try to reinitialize Screen Proctoring for exam: {}", exam.name); + initializeScreenProctoring(exam, apiTemplate); + return exam; + } + // re-activate all needed entities on SPS side if (exam.status == Exam.ExamStatus.RUNNING) { activateScreenProctoring(exam).getOrThrow(); @@ -416,8 +423,26 @@ private void synchronizeFromSEBGroups( } else { log.warn( "Screen Proctoring group mismatch detected. No SPS group found for exam: {} and local group: {}", - exam, + exam.name, existing); + log.info("Try to create new one on SPS"); + try { + final ScreenProctoringGroup groupOnSPS = createGroupOnSPS( + 0, + exam.id, + sebGroup.name, + spsData.spsExamUUID, + existing.isFallback, + sebGroup.id, + apiTemplate); + + this.screenProctoringGroupDAO + .updateFromSPS(existing.id, groupOnSPS) + .getOrThrow(); + + } catch (final Exception e) { + log.error("Failed to synchronize SEB Group on SPS: {}", sebGroup, e); + } } } } @@ -476,11 +501,30 @@ private void synchronizeDefaultGroup( final SPSGroup spsGroup = spsGroups.get(localGroup.uuid); if (spsGroup == null) { + // try re-create group on SPS log.warn( - "Expecting only one default SPS group for exam: {} but there are local groups: {} and groups on SPS: {}", - exam, - localGroups.values(), - spsGroups.values()); + "Screen Proctoring group mismatch detected. No SPS group found for exam: {} and local group: {}", + exam.name, + localGroup); + log.info("Try to create new one on SPS"); + try { + final ScreenProctoringGroup groupOnSPS = createGroupOnSPS( + 0, + exam.id, + localGroup.name, + spsData.spsExamUUID, + localGroup.isFallback, + localGroup.id, + apiTemplate); + + this.screenProctoringGroupDAO + .updateFromSPS(localGroup.id, groupOnSPS) + .getOrThrow(); + + } catch (final Exception e) { + log.error("Failed to synchronize SEB Group on SPS: {}", localGroup, e); + } + return; } // if name has changed synchronize locally @@ -597,9 +641,9 @@ private Collection getSPSGroups( /** This is called when an exam has changed its parameter and needs data update on SPS side * * @param exam The exam*/ - void updateExam(final Exam exam) { - - try { + Result updateExam(final Exam exam) { + + return Result.tryCatch(() -> { final SPSData spsData = this.getSPSData(exam.id); final ScreenProctoringServiceOAuthTemplate apiTemplate = this.getAPITemplate(exam.id); final ScreenProctoringSettings settings = this.proctoringSettingsDAO @@ -621,9 +665,9 @@ void updateExam(final Exam exam) { exam.getType().name(), exam.startTime != null ? exam.startTime.getMillis() : null, exam.endTime != null ? exam.endTime.getMillis() : null, - settings.deletionTime != null ? settings.deletionTime.getMillis() : null, + settings.deletionTime != null ? settings.deletionTime.getMillis() : null, supporterIds - ); + ); final String jsonExamUpdate = this.jsonMapper.writeValueAsString(examUpdate); @@ -632,13 +676,13 @@ void updateExam(final Exam exam) { HttpMethod.PUT, jsonExamUpdate, apiTemplate.getHeadersJSONRequest()); + if (exchange.getStatusCode() != HttpStatus.OK) { - log.error("Failed to update SPS exam data: {}", exchange); + throw new RuntimeException("Failed to update SPS exam data: " + exchange); } - - } catch (final Exception e) { - log.error("Failed to update exam on SPS service for exam: {}", exam, e); - } + + return exam; + }); } /** This is called when an exam finishes and deactivates the Exam, SEB Client Access on Screen Proctoring Service side. @@ -704,7 +748,7 @@ Result activateScreenProctoring(final Exam exam) { private void initializeScreenProctoring( final Exam exam, final ScreenProctoringServiceOAuthTemplate apiTemplate) throws JsonProcessingException { - + final SPSData spsData = new SPSData(); log.info( "SPS Exam for SEB Server Exam: {} don't exists yet, create necessary structures on SPS", @@ -826,7 +870,7 @@ Tuple createSEBSession( final String key = exchange1.getHeaders().getFirst(SESSION_ENCRYPTION_KEY_REQUEST_HEADER); if (StringUtils.isBlank(key)) { - log.error("Failed to get SEB session encryption key form SPS"); + log.error("Failed to get SEB session encryption key from SPS"); } return new Tuple<>(token, key); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java index 4f28a7f10..fa535b20f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java @@ -258,7 +258,7 @@ public Result applyScreenProctoringForExam(final EntityKey entityKey) { this.examDAO.markUpdate(exam.id); } else if (isEnabling) { - this.screenProctoringAPIBinding.updateExam(exam); + this.screenProctoringAPIBinding.updateExam(exam).getOrThrow(); this.screenProctoringAPIBinding.synchronizeGroups(exam); } @@ -343,7 +343,9 @@ public void notifyExamSaved(final Exam exam) { } this.screenProctoringAPIBinding.synchronizeUserAccounts(exam); - this.screenProctoringAPIBinding.updateExam(exam); + this.screenProctoringAPIBinding + .updateExam(exam) + .onError(error -> log.warn("Failed to update exam on SPS: ", error)); this.screenProctoringAPIBinding.synchronizeGroups(exam); } @@ -488,8 +490,6 @@ private void closeScreenProctoringSession(final ClientConnectionRecord ccRecord) } } - - private void applyScreenProctoringSession(final ClientConnectionRecord ccRecord) { try {