From f972485e33beb281ad4f224862072b8a27513394 Mon Sep 17 00:00:00 2001 From: Pyry Koivisto Date: Fri, 18 Oct 2024 14:42:55 +0300 Subject: [PATCH] VKT(Backend): Examiner details controller --- .../examiner/ExaminerDetailsCreateDTO.java | 11 ++ .../api/dto/examiner/ExaminerDetailsDTO.java | 16 +++ .../dto/examiner/ExaminerDetailsInitDTO.java | 7 ++ .../examiner/ExaminerDetailsController.java | 41 +++++++ .../vkt/repository/ExaminerRepository.java | 2 + .../vkt/scheduled/OnrPersonalDataUpdater.java | 6 +- .../vkt/service/ExaminerDetailsService.java | 106 ++++++++++++++++++ .../vkt/service/PublicExaminerService.java | 17 --- .../vkt/util/exception/APIExceptionType.java | 4 +- 9 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsCreateDTO.java create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsDTO.java create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsInitDTO.java create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/api/examiner/ExaminerDetailsController.java create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerDetailsService.java diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsCreateDTO.java b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsCreateDTO.java new file mode 100644 index 000000000..56c1f87b2 --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsCreateDTO.java @@ -0,0 +1,11 @@ +package fi.oph.vkt.api.dto.examiner; + +import lombok.Builder; +import lombok.NonNull; + +@Builder +public record ExaminerDetailsCreateDTO( + @NonNull String email, + @NonNull Boolean examLanguageFinnish, + @NonNull Boolean examLanguageSwedish +) {} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsDTO.java b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsDTO.java new file mode 100644 index 000000000..65c5506c0 --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsDTO.java @@ -0,0 +1,16 @@ +package fi.oph.vkt.api.dto.examiner; + +import lombok.Builder; +import lombok.NonNull; + +@Builder +public record ExaminerDetailsDTO( + @NonNull Long id, + @NonNull Integer version, + @NonNull String oid, + @NonNull String email, + @NonNull String lastName, + @NonNull String firstName, + @NonNull Boolean examLanguageFinnish, + @NonNull Boolean examLanguageSwedish +) {} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsInitDTO.java b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsInitDTO.java new file mode 100644 index 000000000..544fc847a --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/examiner/ExaminerDetailsInitDTO.java @@ -0,0 +1,7 @@ +package fi.oph.vkt.api.dto.examiner; + +import lombok.Builder; +import lombok.NonNull; + +@Builder +public record ExaminerDetailsInitDTO(@NonNull String oid, @NonNull String lastName, @NonNull String firstName) {} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/examiner/ExaminerDetailsController.java b/backend/vkt/src/main/java/fi/oph/vkt/api/examiner/ExaminerDetailsController.java new file mode 100644 index 000000000..bf8891ff9 --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/examiner/ExaminerDetailsController.java @@ -0,0 +1,41 @@ +package fi.oph.vkt.api.examiner; + +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsCreateDTO; +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsDTO; +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsInitDTO; +import fi.oph.vkt.service.ExaminerDetailsService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value = "/api/v1/tv/{oid}", produces = MediaType.APPLICATION_JSON_VALUE) +public class ExaminerDetailsController { + + private static final String TAG_EXAMINER = "Examiner details API"; + + @Resource + private ExaminerDetailsService examinerDetailsService; + + @GetMapping + @Operation(tags = TAG_EXAMINER, summary = "Get examiner details") + public ExaminerDetailsDTO getExaminerDetails(@PathVariable("oid") String oid) { + return examinerDetailsService.getExaminer(oid); + } + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + @Operation(tags = TAG_EXAMINER, summary = "Create examiner") + public ExaminerDetailsDTO createExaminer( + @PathVariable("oid") String oid, + @RequestBody ExaminerDetailsCreateDTO examinerDetailsCreateDTO + ) { + return examinerDetailsService.createExaminer(oid, examinerDetailsCreateDTO); + } + + @GetMapping(path = "/init") + @Operation(tags = TAG_EXAMINER, summary = "Get examiner personal data needed for initializing examiner details") + public ExaminerDetailsInitDTO getInitialExaminerDetails(@PathVariable("oid") String oid) { + return examinerDetailsService.getInitialExaminerPersonalData(oid); + } +} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/repository/ExaminerRepository.java b/backend/vkt/src/main/java/fi/oph/vkt/repository/ExaminerRepository.java index 8ef29618d..8bb8d9b8a 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/repository/ExaminerRepository.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/repository/ExaminerRepository.java @@ -2,6 +2,7 @@ import fi.oph.vkt.model.Examiner; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -9,6 +10,7 @@ public interface ExaminerRepository extends BaseRepository { List getAllByDeletedAtIsNull(); Examiner getByOid(String oid); + Optional findByOid(String oid); @Query("SELECT e.oid FROM Examiner e WHERE e.deletedAt IS NULL") List listExistingOnrIds(); diff --git a/backend/vkt/src/main/java/fi/oph/vkt/scheduled/OnrPersonalDataUpdater.java b/backend/vkt/src/main/java/fi/oph/vkt/scheduled/OnrPersonalDataUpdater.java index b5a762529..55e95cf41 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/scheduled/OnrPersonalDataUpdater.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/scheduled/OnrPersonalDataUpdater.java @@ -1,6 +1,6 @@ package fi.oph.vkt.scheduled; -import fi.oph.vkt.service.PublicExaminerService; +import fi.oph.vkt.service.ExaminerDetailsService; import fi.oph.vkt.util.SchedulingUtil; import jakarta.annotation.Resource; import lombok.RequiredArgsConstructor; @@ -21,14 +21,14 @@ public class OnrPersonalDataUpdater { private static final String LOCK_AT_MOST = "PT3M"; @Resource - private final PublicExaminerService publicExaminerService; + private final ExaminerDetailsService examinerDetailsService; @Scheduled(initialDelayString = INITIAL_DELAY, fixedDelayString = FIXED_DELAY) @SchedulerLock(name = "updatePersonalDataFromOnr", lockAtLeastFor = LOCK_AT_LEAST, lockAtMostFor = LOCK_AT_MOST) public void updateOnrCache() { SchedulingUtil.runWithScheduledUser(() -> { LOG.debug("updatePersonalDataFromOnr"); - publicExaminerService.updateStoredPersonalData(); + examinerDetailsService.updateStoredPersonalData(); }); } } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerDetailsService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerDetailsService.java new file mode 100644 index 000000000..c55336105 --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerDetailsService.java @@ -0,0 +1,106 @@ +package fi.oph.vkt.service; + +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsCreateDTO; +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsDTO; +import fi.oph.vkt.api.dto.examiner.ExaminerDetailsInitDTO; +import fi.oph.vkt.audit.AuditService; +import fi.oph.vkt.model.Examiner; +import fi.oph.vkt.repository.ExaminerRepository; +import fi.oph.vkt.service.onr.OnrService; +import fi.oph.vkt.service.onr.PersonalData; +import fi.oph.vkt.util.exception.APIException; +import fi.oph.vkt.util.exception.APIExceptionType; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ExaminerDetailsService { + + private final ExaminerRepository examinerRepository; + private final OnrService onrService; + private final AuditService auditService; + + private PersonalData getOnrPersonalData(final String oid) { + Map oidToData = onrService.getOnrPersonalData(List.of(oid)); + return oidToData.get(oid); + } + + private static ExaminerDetailsDTO toExaminerDetailsDTO(final Examiner examiner) { + return ExaminerDetailsDTO + .builder() + .id(examiner.getId()) + .version(examiner.getVersion()) + .oid(examiner.getOid()) + .lastName(examiner.getLastName()) + .firstName(examiner.getFirstName()) + .email(examiner.getEmail()) + .examLanguageFinnish(examiner.isExamLanguageFinnish()) + .examLanguageSwedish(examiner.isExamLanguageSwedish()) + .build(); + } + + @Transactional(readOnly = true) + public ExaminerDetailsInitDTO getInitialExaminerPersonalData(final String oid) { + // TODO Audit log entry + if (examinerRepository.findByOid(oid).isPresent()) { + throw new APIException(APIExceptionType.EXAMINER_ALREADY_INITIALIZED); + } + PersonalData personalData = this.getOnrPersonalData(oid); + if (personalData == null) { + throw new APIException(APIExceptionType.EXAMINER_ONR_NOT_FOUND); + } + return ExaminerDetailsInitDTO + .builder() + .oid(oid) + .lastName(personalData.getLastName()) + .firstName(personalData.getFirstName()) + .build(); + } + + @Transactional + public ExaminerDetailsDTO createExaminer(final String oid, ExaminerDetailsCreateDTO examinerDetailsCreateDTO) { + // TODO Audit log entry + if (examinerRepository.findByOid(oid).isPresent()) { + throw new APIException(APIExceptionType.EXAMINER_ALREADY_INITIALIZED); + } + PersonalData personalData = this.getOnrPersonalData(oid); + if (personalData == null) { + throw new APIException(APIExceptionType.EXAMINER_ONR_NOT_FOUND); + } + Examiner examiner = new Examiner(); + examiner.setOid(oid); + examiner.setLastName(personalData.getLastName()); + examiner.setFirstName(personalData.getFirstName()); + examiner.setNickname(personalData.getNickname()); + examiner.setEmail(examinerDetailsCreateDTO.email()); + examiner.setExamLanguageFinnish(examinerDetailsCreateDTO.examLanguageFinnish()); + examiner.setExamLanguageSwedish(examinerDetailsCreateDTO.examLanguageSwedish()); + examinerRepository.saveAndFlush(examiner); + + return toExaminerDetailsDTO(examiner); + } + + @Transactional(readOnly = true) + public ExaminerDetailsDTO getExaminer(final String oid) { + // TODO Audit log entry + Examiner examiner = examinerRepository.getByOid(oid); + return toExaminerDetailsDTO(examiner); + } + + @Transactional + public void updateStoredPersonalData() { + final List onrIds = examinerRepository.listExistingOnrIds(); + final Map oidToPersonalData = onrService.getOnrPersonalData(onrIds); + oidToPersonalData.forEach((k, v) -> { + Examiner examiner = examinerRepository.getByOid(k); + examiner.setLastName(v.getLastName()); + examiner.setFirstName(v.getFirstName()); + examiner.setNickname(v.getNickname()); + examinerRepository.saveAndFlush(examiner); + }); + } +} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicExaminerService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicExaminerService.java index 942b2cfd2..d6ff6faef 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicExaminerService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicExaminerService.java @@ -8,11 +8,8 @@ import fi.oph.vkt.model.ExaminerMunicipality; import fi.oph.vkt.model.type.ExamLanguage; import fi.oph.vkt.repository.ExaminerRepository; -import fi.oph.vkt.service.onr.OnrService; -import fi.oph.vkt.service.onr.PersonalData; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +20,6 @@ public class PublicExaminerService { private final ExaminerRepository examinerRepository; - private final OnrService onrService; private static PublicMunicipalityDTO toPublicMunicipalityDTO(ExaminerMunicipality municipality) { return PublicMunicipalityDTO.builder().fi(municipality.getNameFi()).sv(municipality.getNameSv()).build(); @@ -76,17 +72,4 @@ public List listExaminers() { .map(PublicExaminerService::toPublicExaminerDTO) .collect(Collectors.toList()); } - - @Transactional - public void updateStoredPersonalData() { - final List onrIds = examinerRepository.listExistingOnrIds(); - final Map oidToPersonalData = onrService.getOnrPersonalData(onrIds); - oidToPersonalData.forEach((k, v) -> { - Examiner examiner = examinerRepository.getByOid(k); - examiner.setLastName(v.getLastName()); - examiner.setFirstName(v.getFirstName()); - examiner.setNickname(v.getNickname()); - examinerRepository.saveAndFlush(examiner); - }); - } } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/util/exception/APIExceptionType.java b/backend/vkt/src/main/java/fi/oph/vkt/util/exception/APIExceptionType.java index eda9849a2..ab94336ae 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/util/exception/APIExceptionType.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/util/exception/APIExceptionType.java @@ -34,7 +34,9 @@ public enum APIExceptionType { TOO_MANY_ATTACHMENTS, INVALID_ATTACHMENT, ATTACHMENT_PERSON_MISMATCH, - TICKET_VALIDATION_ERROR; + TICKET_VALIDATION_ERROR, + EXAMINER_ALREADY_INITIALIZED, + EXAMINER_ONR_NOT_FOUND; public String getCode() { final StringBuilder codeBuilder = new StringBuilder();