From ee9872da5253ab9b95c39714a49d55e9c86f4ffa Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Tue, 19 Dec 2023 15:03:23 +0100 Subject: [PATCH] [kbss-cvut/record-manager-ui#38] Allow providing phase to set on all imported records. --- .../cz/cvut/kbss/study/model/RecordPhase.java | 16 ++++++++++++ .../study/rest/PatientRecordController.java | 16 +++++++++--- .../study/service/PatientRecordService.java | 16 ++++++++++++ .../RepositoryPatientRecordService.java | 10 +++++++ .../environment/generator/Generator.java | 1 + .../kbss/study/model/RecordPhaseTest.java | 26 +++++++++++++++++++ .../rest/PatientRecordControllerTest.java | 19 +++++++++++++- .../RepositoryPatientRecordServiceTest.java | 20 ++++++++++++++ 8 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 src/test/java/cz/cvut/kbss/study/model/RecordPhaseTest.java diff --git a/src/main/java/cz/cvut/kbss/study/model/RecordPhase.java b/src/main/java/cz/cvut/kbss/study/model/RecordPhase.java index 398a39d9..20a09a20 100644 --- a/src/main/java/cz/cvut/kbss/study/model/RecordPhase.java +++ b/src/main/java/cz/cvut/kbss/study/model/RecordPhase.java @@ -23,4 +23,20 @@ public enum RecordPhase { public String getIri() { return iri; } + + /** + * Returns {@link RecordPhase} with the specified IRI. + * + * @param iri record phase identifier + * @return matching {@code RecordPhase} + * @throws IllegalArgumentException When no matching phase is found + */ + public static RecordPhase fromString(String iri) { + for (RecordPhase p : values()) { + if (p.getIri().equals(iri)) { + return p; + } + } + throw new IllegalArgumentException("Unknown record phase identifier '" + iri + "'."); + } } diff --git a/src/main/java/cz/cvut/kbss/study/rest/PatientRecordController.java b/src/main/java/cz/cvut/kbss/study/rest/PatientRecordController.java index 94ec33c4..fa0c4bbe 100644 --- a/src/main/java/cz/cvut/kbss/study/rest/PatientRecordController.java +++ b/src/main/java/cz/cvut/kbss/study/rest/PatientRecordController.java @@ -5,6 +5,7 @@ import cz.cvut.kbss.study.exception.NotFoundException; import cz.cvut.kbss.study.model.Institution; import cz.cvut.kbss.study.model.PatientRecord; +import cz.cvut.kbss.study.model.RecordPhase; import cz.cvut.kbss.study.rest.exception.BadRequestException; import cz.cvut.kbss.study.rest.util.RecordFilterMapper; import cz.cvut.kbss.study.rest.util.RestUtils; @@ -96,10 +97,17 @@ public ResponseEntity createRecord(@RequestBody PatientRecord record) { } @PostMapping(value = "/import", consumes = MediaType.APPLICATION_JSON_VALUE) - public RecordImportResult importRecords(@RequestBody List records) { - final RecordImportResult result = recordService.importRecords(records); - LOG.trace("Records imported with result: {}.", result); - return result; + public RecordImportResult importRecords(@RequestBody List records, + @RequestParam(name = "phase", required = false) String phaseIri) { + final RecordImportResult importResult; + if (phaseIri != null) { + final RecordPhase targetPhase = RecordPhase.fromString(phaseIri); + importResult = recordService.importRecords(records, targetPhase); + } else { + importResult = recordService.importRecords(records); + } + LOG.trace("Records imported with result: {}.", importResult); + return importResult; } @PutMapping(value = "/{key}", consumes = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/cz/cvut/kbss/study/service/PatientRecordService.java b/src/main/java/cz/cvut/kbss/study/service/PatientRecordService.java index 6c320727..9887789c 100644 --- a/src/main/java/cz/cvut/kbss/study/service/PatientRecordService.java +++ b/src/main/java/cz/cvut/kbss/study/service/PatientRecordService.java @@ -4,6 +4,7 @@ import cz.cvut.kbss.study.dto.RecordImportResult; import cz.cvut.kbss.study.model.Institution; import cz.cvut.kbss.study.model.PatientRecord; +import cz.cvut.kbss.study.model.RecordPhase; import cz.cvut.kbss.study.model.User; import cz.cvut.kbss.study.persistence.dao.util.RecordFilterParams; @@ -59,9 +60,24 @@ public interface PatientRecordService extends BaseService { * The current user is set as the author of the records. Only records whose identifiers do not already exist in the * repository are imported. Existing records are skipped and the returned object contains a note that the record * already exists. + *

+ * This method, in contrast to {@link #importRecords(List, RecordPhase)}, preserves the phase of the imported + * records. * * @param records Records to import * @return Instance representing the import result */ RecordImportResult importRecords(List records); + + /** + * Imports the specified records and sets them all to the specified phase. + *

+ * The current user is set as the author of the records. Only records whose identifiers do not already exist in the + * repository are imported. Existing records are skipped and the returned object contains a note that the record + * already exists. + * + * @param records Records to import + * @return Instance representing the import result + */ + RecordImportResult importRecords(List records, RecordPhase targetPhase); } diff --git a/src/main/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordService.java b/src/main/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordService.java index 4f529f23..1684cf37 100644 --- a/src/main/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordService.java +++ b/src/main/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordService.java @@ -4,6 +4,7 @@ import cz.cvut.kbss.study.dto.RecordImportResult; import cz.cvut.kbss.study.model.Institution; import cz.cvut.kbss.study.model.PatientRecord; +import cz.cvut.kbss.study.model.RecordPhase; import cz.cvut.kbss.study.model.User; import cz.cvut.kbss.study.persistence.dao.OwlKeySupportingDao; import cz.cvut.kbss.study.persistence.dao.PatientRecordDao; @@ -103,4 +104,13 @@ public RecordImportResult importRecords(List records) { }); return result; } + + @Transactional + @Override + public RecordImportResult importRecords(List records, RecordPhase targetPhase) { + Objects.requireNonNull(records); + LOG.debug("Importing records to target phase '{}'.", targetPhase); + records.forEach(r -> r.setPhase(targetPhase)); + return importRecords(records); + } } diff --git a/src/test/java/cz/cvut/kbss/study/environment/generator/Generator.java b/src/test/java/cz/cvut/kbss/study/environment/generator/Generator.java index 77b67ac7..2a8b5693 100644 --- a/src/test/java/cz/cvut/kbss/study/environment/generator/Generator.java +++ b/src/test/java/cz/cvut/kbss/study/environment/generator/Generator.java @@ -170,6 +170,7 @@ public static PatientRecord generatePatientRecord(User author) { rec.setLocalName("RandomRecord" + randomInt()); rec.setUri(generateUri()); rec.setInstitution(author.getInstitution()); + rec.setPhase(RecordPhase.values()[Generator.randomInt(0, RecordPhase.values().length)]); return rec; } diff --git a/src/test/java/cz/cvut/kbss/study/model/RecordPhaseTest.java b/src/test/java/cz/cvut/kbss/study/model/RecordPhaseTest.java new file mode 100644 index 00000000..59ad29af --- /dev/null +++ b/src/test/java/cz/cvut/kbss/study/model/RecordPhaseTest.java @@ -0,0 +1,26 @@ +package cz.cvut.kbss.study.model; + +import cz.cvut.kbss.study.environment.generator.Generator; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RecordPhaseTest { + + @Test + void fromStringReturnsMatchingRecordPhase() { + for (RecordPhase p : RecordPhase.values()) { + assertEquals(p, RecordPhase.fromString(p.getIri())); + } + } + + @Test + void fromStringThrowsIllegalArgumentForUnknownPhaseIri() { + assertThrows(IllegalArgumentException.class, () -> RecordPhase.fromString(Generator.generateUri().toString())); + } + + @Test + void fromStringThrowsIllegalArgumentForNullArgument() { + assertThrows(IllegalArgumentException.class, () -> RecordPhase.fromString(null)); + } +} \ No newline at end of file diff --git a/src/test/java/cz/cvut/kbss/study/rest/PatientRecordControllerTest.java b/src/test/java/cz/cvut/kbss/study/rest/PatientRecordControllerTest.java index f80c076a..92099428 100644 --- a/src/test/java/cz/cvut/kbss/study/rest/PatientRecordControllerTest.java +++ b/src/test/java/cz/cvut/kbss/study/rest/PatientRecordControllerTest.java @@ -7,6 +7,7 @@ import cz.cvut.kbss.study.environment.util.Environment; import cz.cvut.kbss.study.model.Institution; import cz.cvut.kbss.study.model.PatientRecord; +import cz.cvut.kbss.study.model.RecordPhase; import cz.cvut.kbss.study.model.User; import cz.cvut.kbss.study.persistence.dao.util.RecordFilterParams; import cz.cvut.kbss.study.service.InstitutionService; @@ -30,19 +31,21 @@ import static cz.cvut.kbss.study.environment.util.ContainsSameEntities.containsSameEntities; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.Matchers.anyOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) public class PatientRecordControllerTest extends BaseControllerTestRunner { @@ -315,4 +318,18 @@ void importRecordsImportsSpecifiedRecordsAndReturnsImportResult() throws Excepti verify(patientRecordServiceMock).importRecords(captor.capture()); assertEquals(records.size(), captor.getValue().size()); } + + @Test + void importRecordsImportsSpecifiedRecordsWithSpecifiedPhaseAndReturnsImportResult() throws Exception { + final List records = + List.of(Generator.generatePatientRecord(user), Generator.generatePatientRecord(user)); + final RecordImportResult importResult = new RecordImportResult(records.size()); + importResult.setImportedCount(records.size()); + final RecordPhase targetPhase = RecordPhase.values()[Generator.randomInt(0, RecordPhase.values().length)]; + when(patientRecordServiceMock.importRecords(anyList(), any(RecordPhase.class))).thenReturn(importResult); + + mockMvc.perform(post("/records/import").content(toJson(records)).contentType(MediaType.APPLICATION_JSON) + .param("phase", targetPhase.getIri())).andExpect(status().isOk()); + verify(patientRecordServiceMock).importRecords(anyList(), eq(targetPhase)); + } } diff --git a/src/test/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordServiceTest.java b/src/test/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordServiceTest.java index 49688a13..eae9688d 100644 --- a/src/test/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordServiceTest.java +++ b/src/test/java/cz/cvut/kbss/study/service/repository/RepositoryPatientRecordServiceTest.java @@ -4,6 +4,7 @@ import cz.cvut.kbss.study.environment.generator.Generator; import cz.cvut.kbss.study.environment.util.Environment; import cz.cvut.kbss.study.model.PatientRecord; +import cz.cvut.kbss.study.model.RecordPhase; import cz.cvut.kbss.study.model.User; import cz.cvut.kbss.study.persistence.dao.PatientRecordDao; import cz.cvut.kbss.study.service.security.SecurityUtils; @@ -100,4 +101,23 @@ void importRecordsSkipsImportingRecordsThatAlreadyExist() { assertEquals(1, result.getErrors().size()); toImport.forEach(r -> verify(recordDao).exists(r.getUri())); } + + @Test + void importRecordsWithPhaseSetsSpecifiedPhaseToAllRecords() { + final List toImport = + List.of(Generator.generatePatientRecord(user), Generator.generatePatientRecord(user)); + final User originalAuthor = Generator.generateUser(Generator.generateInstitution()); + final RecordPhase targetPhase = RecordPhase.values()[Generator.randomInt(0, RecordPhase.values().length)]; + toImport.forEach(r -> { + // Simulate that the records existed in another deployment from which they are imported + r.setKey(IdentificationUtils.generateKey()); + r.setDateCreated(new Date(System.currentTimeMillis() - 10000L)); + r.setAuthor(originalAuthor); + }); + + sut.importRecords(toImport, targetPhase); + final ArgumentCaptor captor = ArgumentCaptor.forClass(PatientRecord.class); + verify(recordDao, times(toImport.size())).persist(captor.capture()); + captor.getAllValues().forEach(r -> assertEquals(targetPhase, r.getPhase())); + } } \ No newline at end of file