From 6c54dd7c8a779a578fc0370f4d0e683a6bcd352e Mon Sep 17 00:00:00 2001 From: Jakob Frank Date: Sun, 12 May 2024 17:20:59 +0200 Subject: [PATCH] #224 Study-Preview Mode: Cleanup after Preview - Reset Participant-Status - Recreate Registration-Token - Clear Study-Data -> Tell the Gateway to collect data in Preview-Mode --- .../model/transformer/StudyTransformer.java | 9 ++++---- .../repository/ParticipantRepository.java | 22 ++++++++++++++++--- .../repository/RepositoryUtils.java | 9 ++++++++ .../studymanager/service/ElasticService.java | 8 ++++++- .../service/ParticipantService.java | 9 +++----- .../studymanager/service/StudyService.java | 5 +++-- .../V1_15_1__extend_gateway_for_preview.sql | 9 ++++++++ 7 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 studymanager/src/main/resources/db/migration/V1_15_1__extend_gateway_for_preview.sql diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/model/transformer/StudyTransformer.java b/studymanager/src/main/java/io/redlink/more/studymanager/model/transformer/StudyTransformer.java index 5253deb2..24ac7edd 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/model/transformer/StudyTransformer.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/model/transformer/StudyTransformer.java @@ -8,13 +8,14 @@ */ package io.redlink.more.studymanager.model.transformer; -import io.redlink.more.studymanager.api.v1.model.*; +import io.redlink.more.studymanager.api.v1.model.ContactDTO; +import io.redlink.more.studymanager.api.v1.model.StatusChangeDTO; +import io.redlink.more.studymanager.api.v1.model.StudyDTO; +import io.redlink.more.studymanager.api.v1.model.StudyStatusDTO; import io.redlink.more.studymanager.model.Contact; import io.redlink.more.studymanager.model.Study; import io.redlink.more.studymanager.model.scheduler.Duration; -import java.util.Optional; - public class StudyTransformer { private StudyTransformer() {} @@ -60,6 +61,6 @@ public static StudyDTO toStudyDTO_V1(Study study) { } public static Study.Status fromStatusChangeDTO_V1(StatusChangeDTO statusChangeDTO) { - return Study.Status.valueOf(statusChangeDTO.getStatus().getValue().toUpperCase()); + return Study.Status.fromValue(statusChangeDTO.getStatus().getValue()); } } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/repository/ParticipantRepository.java b/studymanager/src/main/java/io/redlink/more/studymanager/repository/ParticipantRepository.java index 046ec8a0..de03f205 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/repository/ParticipantRepository.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/repository/ParticipantRepository.java @@ -8,6 +8,7 @@ */ package io.redlink.more.studymanager.repository; +import com.google.common.base.Supplier; import io.redlink.more.studymanager.exception.BadRequestException; import io.redlink.more.studymanager.model.Participant; import java.util.List; @@ -24,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional; import static io.redlink.more.studymanager.repository.RepositoryUtils.getValidNullableIntegerValue; +import static io.redlink.more.studymanager.repository.RepositoryUtils.intReader; @Component public class ParticipantRepository { @@ -136,9 +138,23 @@ public void cleanupParticipants(Long studyId) { namedTemplate.update("DELETE FROM push_notifications_token WHERE study_id = :study_id", params); } - public void updateRegistrationToken(Long studyId, Integer participantId, String registrationToken) { - namedTemplate.update(UPDATE_REGISTRATION_TOKEN, - toParams(studyId, participantId).addValue("token", registrationToken)); + @Transactional + public void resetParticipants(final Long studyId, final Supplier tokenSource) { + // First clear credentials and tokens... + cleanupParticipants(studyId); + // ... then reset participant-status and start-date ... + final var pIDs = namedTemplate.query( + "UPDATE participants SET status = DEFAULT, start = NULL WHERE study_id = :study_id RETURNING *", + toParams(studyId), + intReader("participant_id") + ); + // ... and finally create new token for the participants + namedTemplate.batchUpdate( + UPDATE_REGISTRATION_TOKEN, + pIDs.stream() + .map(pid -> toParams(studyId, pid).addValue("token", tokenSource.get())) + .toArray(MapSqlParameterSource[]::new) + ); } public void clear() { diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/repository/RepositoryUtils.java b/studymanager/src/main/java/io/redlink/more/studymanager/repository/RepositoryUtils.java index 384e6f74..5c6154d3 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/repository/RepositoryUtils.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/repository/RepositoryUtils.java @@ -11,6 +11,7 @@ import java.sql.SQLException; import java.time.Instant; import java.time.LocalDate; +import org.springframework.jdbc.core.RowMapper; public final class RepositoryUtils { @@ -69,4 +70,12 @@ public static String toParam(Participant.Status status) { case LOCKED -> "locked"; }; } + + public static RowMapper intReader(String columnLabel) { + return (rs, rowNum) -> { + int anInt = rs.getInt(columnLabel); + if (rs.wasNull()) return null; + return anInt; + }; + } } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/service/ElasticService.java b/studymanager/src/main/java/io/redlink/more/studymanager/service/ElasticService.java index 72ddb09d..376bd9d9 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/service/ElasticService.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/service/ElasticService.java @@ -29,6 +29,7 @@ import io.redlink.more.studymanager.model.data.SimpleDataPoint; import io.redlink.more.studymanager.properties.ElasticProperties; import io.redlink.more.studymanager.utils.MapperUtils; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -265,7 +266,12 @@ public List getParticipationData(Long studyId){ } return participationDataList; }catch (IOException | ElasticsearchException e) { - LOG.error("Elastic Query failed", e); + if (e instanceof ElasticsearchException ee) { + if (Objects.equals(ee.error().type(), "index_not_found_exception")) { + return List.of(); + } + } + LOG.warn("Elastic Query failed", e); return new ArrayList<>(); } } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/service/ParticipantService.java b/studymanager/src/main/java/io/redlink/more/studymanager/service/ParticipantService.java index 19df9a37..af812635 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/service/ParticipantService.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/service/ParticipantService.java @@ -68,14 +68,11 @@ public Participant updateParticipant(Participant participant) { @Transactional public void alignParticipantsWithStudyState(Study study) { - if (EnumSet.of(Study.Status.CLOSED, Study.Status.DRAFT).contains(study.getStudyState())) { + if (EnumSet.of(Study.Status.CLOSED).contains(study.getStudyState())) { participantRepository.cleanupParticipants(study.getStudyId()); } - if (EnumSet.of(Study.Status.PREVIEW).contains(study.getStudyState())) { - participantRepository.listParticipants(study.getStudyId()) - .forEach(p -> participantRepository.updateRegistrationToken( - p.getStudyId(), p.getParticipantId(), RandomTokenGenerator.generate() - )); + if (EnumSet.of(Study.Status.DRAFT).contains(study.getStudyState())) { + participantRepository.resetParticipants(study.getStudyId(), RandomTokenGenerator::generate); } } diff --git a/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java b/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java index 2ba89bb0..85e1a848 100644 --- a/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java +++ b/studymanager/src/main/java/io/redlink/more/studymanager/service/StudyService.java @@ -32,9 +32,9 @@ public class StudyService { private static final Map> VALID_STUDY_TRANSITIONS = Map.of( Study.Status.DRAFT, EnumSet.of(Study.Status.PREVIEW, Study.Status.ACTIVE), Study.Status.PREVIEW, EnumSet.of(Study.Status.PAUSED_PREVIEW, Study.Status.DRAFT), - Study.Status.PAUSED_PREVIEW, EnumSet.of(Study.Status.DRAFT), + Study.Status.PAUSED_PREVIEW, EnumSet.of(Study.Status.PREVIEW, Study.Status.DRAFT), Study.Status.ACTIVE, EnumSet.of(Study.Status.PAUSED, Study.Status.CLOSED), - Study.Status.PAUSED, EnumSet.of(Study.Status.ACTIVE) + Study.Status.PAUSED, EnumSet.of(Study.Status.ACTIVE, Study.Status.CLOSED) ); private final StudyRepository studyRepository; @@ -128,6 +128,7 @@ public void setStatus(Long studyId, Study.Status newState, User user) { ) ); participantService.alignParticipantsWithStudyState(s); + elasticService.deleteIndex(s.getStudyId()); } catch (Exception e) { log.warn("Could not set new state for study id {}; old state: {}; new state: {}", studyId, oldState.getValue(), s.getStudyState().getValue()); //ROLLBACK diff --git a/studymanager/src/main/resources/db/migration/V1_15_1__extend_gateway_for_preview.sql b/studymanager/src/main/resources/db/migration/V1_15_1__extend_gateway_for_preview.sql new file mode 100644 index 00000000..2cc80424 --- /dev/null +++ b/studymanager/src/main/resources/db/migration/V1_15_1__extend_gateway_for_preview.sql @@ -0,0 +1,9 @@ +-- Update gateway-view to consider preview +CREATE OR REPLACE VIEW auth_routing_info (api_id, api_secret, study_id, participant_id, study_group_id, study_is_active) AS +SELECT api_credentials.*, pt.study_group_id, s.status IN ('active', 'preview') +FROM api_credentials + INNER JOIN participants pt + ON (api_credentials.study_id = pt.study_id and api_credentials.participant_id = pt.participant_id) + INNER JOIN studies s + ON (api_credentials.study_id = s.study_id) +;