Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 75 additions & 64 deletions src/main/java/cz/cvut/kbss/study/rest/PatientRecordController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import cz.cvut.kbss.study.dto.PatientRecordDto;
import cz.cvut.kbss.study.dto.RecordImportResult;
import cz.cvut.kbss.study.exception.NotFoundException;
import cz.cvut.kbss.study.exception.ValidationException;
import cz.cvut.kbss.study.model.PatientRecord;
import cz.cvut.kbss.study.model.RecordPhase;
import cz.cvut.kbss.study.model.User;
Expand Down Expand Up @@ -41,6 +42,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.sql.Array;
import java.util.*;
import java.util.stream.Stream;

Expand All @@ -61,7 +63,7 @@ public class PatientRecordController extends BaseController {
public PatientRecordController(PatientRecordService recordService, ApplicationEventPublisher eventPublisher,
ExcelRecordConverter excelRecordConverter, RestTemplate restTemplate,
ConfigReader configReader, ObjectMapper objectMapper,
UserService userService) {
UserService userService) {
this.recordService = recordService;
this.eventPublisher = eventPublisher;
this.excelRecordConverter = excelRecordConverter;
Expand All @@ -78,7 +80,7 @@ public List<PatientRecordDto> getRecords(
@RequestParam MultiValueMap<String, String> params,
UriComponentsBuilder uriBuilder, HttpServletResponse response) {
final Page<PatientRecordDto> result = recordService.findAll(RecordFilterMapper.constructRecordFilter(params),
RestUtils.resolvePaging(params));
RestUtils.resolvePaging(params));
eventPublisher.publishEvent(new PaginatedResultRetrievedEvent(this, uriBuilder, response, result));
return result.getContent();
}
Expand All @@ -98,25 +100,25 @@ public ResponseEntity<?> exportRecords(
.map(o -> o.filter(l -> !l.isEmpty()))
.filter(Optional::isPresent)
.map(o -> o.map(l -> l.stream().flatMap(s ->
MediaType.parseMediaTypes(s).stream()
.filter(RestUtils::isSupportedExportType)
).max(Comparator.comparing(MediaType::getQualityValue)).orElse(null)))
MediaType.parseMediaTypes(s).stream()
.filter(RestUtils::isSupportedExportType)
).max(Comparator.comparing(MediaType::getQualityValue)).orElse(null)))
.filter(Optional::isPresent)
.map(o -> o.orElse(null))
.findFirst()
.orElse(MediaType.APPLICATION_JSON)
.removeQualityValue();

return switch (exportType.toString()){
case Constants.MEDIA_TYPE_EXCEL -> exportRecordsExcel(params, uriBuilder, response);
return switch (exportType.toString()) {
case Constants.MEDIA_TYPE_EXCEL -> exportRecordsExcel(params, uriBuilder, response);
case MediaType.APPLICATION_JSON_VALUE -> exportRecordsAsJson(params, uriBuilder, response);
default -> throw new IllegalArgumentException("Unsupported export type: " + exportType);
};
}

protected ResponseEntity<List<PatientRecord>> exportRecordsAsJson(
MultiValueMap<String, String> params,
UriComponentsBuilder uriBuilder, HttpServletResponse response){
UriComponentsBuilder uriBuilder, HttpServletResponse response) {
final Page<PatientRecord> result = recordService.findAllFull(RecordFilterMapper.constructRecordFilter(params),
RestUtils.resolvePaging(params));
eventPublisher.publishEvent(new PaginatedResultRetrievedEvent(this, uriBuilder, response, result));
Expand All @@ -126,7 +128,7 @@ protected ResponseEntity<List<PatientRecord>> exportRecordsAsJson(
}

public ResponseEntity<InputStreamResource> exportRecordsExcel(MultiValueMap<String, String> params,
UriComponentsBuilder uriBuilder, HttpServletResponse response){
UriComponentsBuilder uriBuilder, HttpServletResponse response) {
RecordFilterParams filterParams = new RecordFilterParams();
filterParams.setMinModifiedDate(null);
filterParams.setMaxModifiedDate(null);
Expand Down Expand Up @@ -157,13 +159,11 @@ private PatientRecord findInternal(String key) {
return record;
}



@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<String> createRecord(@RequestBody PatientRecord record) {

if(userService.getCurrentUser().getInstitution() == null)
if (userService.getCurrentUser().getInstitution() == null)
return ResponseEntity.status(HttpStatus.CONFLICT).body("User is not assigned to any institution");

recordService.persist(record);
Expand All @@ -176,22 +176,22 @@ public ResponseEntity<String> createRecord(@RequestBody PatientRecord record) {
}

@PreAuthorize(
"hasRole('" + SecurityConstants.ROLE_ADMIN + "') or @securityUtils.isMemberOfInstitution(#institutionKey)")
"hasRole('" + SecurityConstants.ROLE_ADMIN + "') or @securityUtils.isMemberOfInstitution(#institutionKey)")
@PostMapping(value = "/publish", produces = {MediaType.APPLICATION_JSON_VALUE})
public RecordImportResult publishRecords(
@RequestParam(name = "institution", required = false) String institutionKey,
@RequestParam(required = false) MultiValueMap<String, String> params,
HttpServletRequest request) {
@RequestParam(name = "institution", required = false) String institutionKey,
@RequestParam(required = false) MultiValueMap<String, String> params,
HttpServletRequest request) {

String onPublishRecordsServiceUrl = configReader.getConfig(ConfigParam.ON_PUBLISH_RECORDS_SERVICE_URL);
if(onPublishRecordsServiceUrl == null || onPublishRecordsServiceUrl.isBlank()) {
if (onPublishRecordsServiceUrl == null || onPublishRecordsServiceUrl.isBlank()) {
LOG.info("No publish service url provided, noop.");
RecordImportResult result = new RecordImportResult(0);
result.addError("Cannot publish completed records. Publish server not configured.");
return result;
}

// export
// export
final Page<PatientRecord> result = recordService.findAllFull(RecordFilterMapper.constructRecordFilter(params),
RestUtils.resolvePaging(params));
List<PatientRecord> records = result.getContent();
Expand Down Expand Up @@ -227,40 +227,66 @@ public String getFilename() {
// Call the import endpoint
LOG.debug("Publishing records.");
ResponseEntity<RecordImportResult> responseEntity = restTemplate.postForEntity(
onPublishRecordsServiceUrl, requestEntity, RecordImportResult.class);
onPublishRecordsServiceUrl, requestEntity, RecordImportResult.class);

// TODO make records published

LOG.debug("Publish server response: ", responseEntity.getBody());
return responseEntity.getBody();
}

@PostMapping(value = "/import/json", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public RecordImportResult importRecordsJson(@RequestPart("file") MultipartFile file,
@RequestParam(name = "phase", required = false) String phase) {
@PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public RecordImportResult importRecords(
@RequestPart("file") MultipartFile file,
@RequestParam(name = "phase", required = false) String phase) {

if (file.isEmpty()) {
throw new ValidationException("Cannot import records, missing input file");
}

String filename = file.getOriginalFilename();

if (filename == null) {
throw new ValidationException("Cannot import records, missing filename");
}

List<PatientRecord> records;

if(file.isEmpty())
throw new IllegalArgumentException("Cannot import records, missing input file");
if (filename.endsWith(".json")) {
records = getRecordsFromJson(file, phase);
} else if (filename.endsWith(".xls") || filename.endsWith(".xlsx")) {
records = getRecordsFromExcel(file, phase);
} else if (filename.endsWith(".tsv")) {
records = getRecordsFromTsv(file, phase);
} else {
throw new IllegalArgumentException("Unsupported file type: " + filename);
}

final RecordImportResult importResult;
if (phase != null) {
final RecordPhase targetPhase = RecordPhase.fromIriOrName(phase);
importResult = recordService.importRecords(records, targetPhase);
} else {
importResult = recordService.importRecords(records);
}
LOG.trace("Records imported with result: {}.", importResult);
return importResult;
}

private List<PatientRecord> getRecordsFromJson(MultipartFile file, String phase) {
List<PatientRecord> records;
try {
records = objectMapper.readValue(file.getBytes(), new TypeReference<List<PatientRecord>>(){});
return objectMapper.readValue(file.getBytes(), new TypeReference<List<PatientRecord>>() {
});
} catch (IOException e) {
throw new RuntimeException("Failed to parse JSON content", e);
}
return importRecords(records, phase);
}

@PostMapping(value = "/import/excel", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public RecordImportResult importRecordsExcel(
@RequestPart("file") MultipartFile file,
@RequestParam(name = "phase", required = false) String phase) {
private List<PatientRecord> getRecordsFromExcel(MultipartFile file, String phase) {

List<PatientRecord> records;

if(file.isEmpty())
throw new IllegalArgumentException("Cannot import records, missing input file");

String excelImportServiceUrl = configReader.getConfig(ConfigParam.EXCEL_IMPORT_SERVICE_URL);

if (excelImportServiceUrl == null)
Expand All @@ -273,29 +299,26 @@ public RecordImportResult importRecordsExcel(
body.add("files", file.getResource());

String request = UriComponentsBuilder.fromHttpUrl(excelImportServiceUrl)
.queryParam("datasetResource", "@%s".formatted(file.getOriginalFilename()))
.toUriString();
.queryParam("datasetResource", "@%s".formatted(file.getOriginalFilename()))
.toUriString();

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

ResponseEntity<List<PatientRecord>> responseEntity = restTemplate.exchange(
URI.create(request),
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<List<PatientRecord>>() {}
new ParameterizedTypeReference<List<PatientRecord>>() {
}
);
records = responseEntity.getBody();
return importRecords(records, phase);
return responseEntity.getBody();
}

@PostMapping(value = "/import/tsv", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public RecordImportResult importRecordsTsv(
@RequestPart("file") MultipartFile file,
@RequestParam(name = "phase", required = false) String phase) {
private List<PatientRecord> getRecordsFromTsv(MultipartFile file, String phase) {

List<PatientRecord> records;

if(file.isEmpty())
if (file.isEmpty())
throw new IllegalArgumentException("Cannot import records, missing input file");

String excelImportServiceUrl = configReader.getConfig(ConfigParam.EXCEL_IMPORT_SERVICE_URL);
Expand All @@ -310,15 +333,15 @@ public RecordImportResult importRecordsTsv(
body.add("files", file.getResource());

String request = UriComponentsBuilder.fromHttpUrl(excelImportServiceUrl)
.queryParam("datasetResource", "@%s".formatted(file.getOriginalFilename()))
.toUriString();
.queryParam("datasetResource", "@%s".formatted(file.getOriginalFilename()))
.toUriString();

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

ResponseEntity<byte[]> responseEntity = restTemplate.postForEntity(
URI.create(request),
requestEntity,
byte[].class
URI.create(request),
requestEntity,
byte[].class
);

LOG.info("Import finished with status {}", responseEntity.getStatusCode());
Expand All @@ -327,19 +350,7 @@ public RecordImportResult importRecordsTsv(
LOG.debug("Response body length is {}", responseBody.length);
}

return new RecordImportResult();
}

public RecordImportResult importRecords(List<PatientRecord> records, String phase) {
final RecordImportResult importResult;
if (phase != null) {
final RecordPhase targetPhase = RecordPhase.fromIriOrName(phase);
importResult = recordService.importRecords(records, targetPhase);
} else {
importResult = recordService.importRecords(records);
}
LOG.trace("Records imported with result: {}.", importResult);
return importResult;
return new ArrayList<PatientRecord>();
}


Expand All @@ -359,13 +370,13 @@ public void updateRecord(@PathVariable("key") String key, @RequestBody PatientRe
onUpdateRecord(record.getUri());
}

private void onUpdateRecord(URI uri){
private void onUpdateRecord(URI uri) {
Objects.nonNull(uri);
String onRecordUpdateServiceUrl = Optional.ofNullable(configReader)
.map(r -> r.getConfig(ConfigParam.ON_UPDATE_RECORD_SERVICE_URL))
.orElse(null);

if(onRecordUpdateServiceUrl == null || onRecordUpdateServiceUrl.isBlank()) {
if (onRecordUpdateServiceUrl == null || onRecordUpdateServiceUrl.isBlank()) {
LOG.debug("No onRecordUpdateServiceUrl service url provided, noop.");
return;
}
Expand All @@ -374,7 +385,7 @@ private void onUpdateRecord(URI uri){
String requestUrl = UriComponentsBuilder.fromHttpUrl(onRecordUpdateServiceUrl)
.queryParam("record", uri)
.toUriString();
restTemplate.getForObject(requestUrl,String.class);
restTemplate.getForObject(requestUrl, String.class);
}

@DeleteMapping(value = "/{key}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ void importRecordsJsonImportsSpecifiedRecordsAndReturnsImportResult() throws Exc
MediaType.MULTIPART_FORM_DATA_VALUE, toJson(records).getBytes());

final MvcResult mvcResult = mockMvc.perform(
multipart("/records/import/json")
multipart("/records/import")
.file(file)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
).andReturn();
Expand Down Expand Up @@ -370,7 +370,7 @@ void importRecordsJsonImportsSpecifiedRecordsWithSpecifiedPhaseAndReturnsImportR
MediaType.MULTIPART_FORM_DATA_VALUE, toJson(records).getBytes());

mockMvc.perform(
multipart("/records/import/json")
multipart("/records/import")
.file(file)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.param("phase", targetPhase.getIri())
Expand All @@ -389,7 +389,7 @@ void importRecordsJsonReturnsConflictWhenServiceThrowsRecordAuthorNotFound() thr
MediaType.MULTIPART_FORM_DATA_VALUE, toJson(records).getBytes());

mockMvc.perform(
multipart("/records/import/json")
multipart("/records/import")
.file(file)
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
).andExpect(status().isConflict());
Expand Down