Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JN-1467] doc request admin ux #1203

Open
wants to merge 20 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package bio.terra.pearl.api.admin.controller.file;

import bio.terra.pearl.api.admin.api.ParticipantFileApi;
import bio.terra.pearl.api.admin.service.auth.AuthUtilService;
import bio.terra.pearl.api.admin.service.auth.context.PortalEnrolleeAuthContext;
import bio.terra.pearl.api.admin.service.file.ParticipantFileExtService;
import bio.terra.pearl.core.model.EnvironmentName;
import bio.terra.pearl.core.model.admin.AdminUser;
import bio.terra.pearl.core.model.file.ParticipantFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import java.io.InputStream;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;

@Controller
public class ParticipantFileController implements ParticipantFileApi {
private final ParticipantFileExtService participantFileExtService;
private final HttpServletRequest request;
private final ObjectMapper objectMapper;
private final AuthUtilService authUtilService;

public ParticipantFileController(
ParticipantFileExtService participantFileExtService,
HttpServletRequest request,
ObjectMapper objectMapper,
AuthUtilService authUtilService) {
this.participantFileExtService = participantFileExtService;
this.request = request;
this.objectMapper = objectMapper;
this.authUtilService = authUtilService;
}

@Override
public ResponseEntity<Resource> download(
String portalShortcode,
String studyShortcode,
String envName,
String enrolleeShortcode,
String fileName) {
AdminUser adminUser = authUtilService.requireAdminUser(request);
PortalEnrolleeAuthContext authContext =
PortalEnrolleeAuthContext.of(
adminUser,
portalShortcode,
studyShortcode,
EnvironmentName.valueOf(envName),
enrolleeShortcode);

ParticipantFile participantFile = participantFileExtService.get(authContext, fileName);

InputStream content = participantFileExtService.downloadFile(authContext, fileName);

MediaType mediaType;
try {
mediaType = MediaType.parseMediaType(participantFile.getFileType());
} catch (Exception e) {
mediaType = MediaType.APPLICATION_OCTET_STREAM;
}

return ResponseEntity.ok().contentType(mediaType).body(new InputStreamResource(content));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package bio.terra.pearl.api.admin.service.file;

import bio.terra.pearl.api.admin.service.auth.EnforcePortalEnrolleePermission;
import bio.terra.pearl.api.admin.service.auth.context.PortalEnrolleeAuthContext;
import bio.terra.pearl.core.model.file.ParticipantFile;
import bio.terra.pearl.core.service.exception.NotFoundException;
import bio.terra.pearl.core.service.file.ParticipantFileService;
import bio.terra.pearl.core.service.file.backends.FileStorageBackend;
import bio.terra.pearl.core.service.file.backends.FileStorageBackendProvider;
import java.io.InputStream;
import org.springframework.stereotype.Service;

@Service
public class ParticipantFileExtService {

private final ParticipantFileService participantFileService;
private final FileStorageBackend fileStorageBackend;

public ParticipantFileExtService(
ParticipantFileService participantFileService,
FileStorageBackendProvider fileStorageBackendProvider) {
this.participantFileService = participantFileService;
this.fileStorageBackend = fileStorageBackendProvider.get();
}

@EnforcePortalEnrolleePermission(permission = "participant_data_view")
public InputStream downloadFile(PortalEnrolleeAuthContext authContext, String fileName) {
ParticipantFile participantFile = get(authContext, fileName);

return fileStorageBackend.downloadFile(participantFile.getExternalFileId());
}

@EnforcePortalEnrolleePermission(permission = "participant_data_view")
public ParticipantFile get(PortalEnrolleeAuthContext authContext, String fileName) {
return participantFileService
.findByEnrolleeIdAndFileName(authContext.getEnrollee().getId(), fileName)
.orElseThrow(() -> new NotFoundException("File not found"));
}
}
21 changes: 21 additions & 0 deletions api-admin/src/main/resources/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,27 @@ paths:
format: binary
'500':
$ref: '#/components/responses/ServerError'
/api/portals/v1/{portalShortcode}/studies/{studyShortcode}/env/{envName}/enrollees/{enrolleeShortcode}/file/{fileName}:
get:
summary: Returns the binary file data for the file of the given shortcode
tags: [ participantFile ]
operationId: download
parameters:
- *portalShortcodeParam
- *studyShortcodeParam
- *envNameParam
- *enrolleeShortcodeParam
- { name: fileName, in: path, required: true, schema: { type: string } }
responses:
'200':
description: file data. Note that "version" supports both integers and the string 'latest' to get the most recent
content:
application/*: # any file type
schema:
type: string
format: binary
'500':
$ref: '#/components/responses/ServerError'
/api/portals/v1/{portalShortcode}/studies/{studyShortcode}/env/{envName}/enrolleesWithKits:
get:
summary: Gets a list of enrollees with tasks and kit requests
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package bio.terra.pearl.core.dao.file;

import bio.terra.pearl.core.dao.BaseJdbiDao;
import bio.terra.pearl.core.model.file.ParticipantFile;
import org.jdbi.v3.core.Jdbi;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Component
public class ParticipantFileDao extends BaseJdbiDao<ParticipantFile> {
public ParticipantFileDao(Jdbi jdbi) {
super(jdbi);
}

@Override
protected Class<ParticipantFile> getClazz() {
return ParticipantFile.class;
}

public List<ParticipantFile> findBySurveyResponseId(UUID surveyResponseId) {
return jdbi.withHandle(handle ->
handle.createQuery("""
select file.* from %s file
inner join participant_file_survey_response file_response on file.id = file_response.participant_file_id
where file_response.survey_response_id = :surveyResponseId
""".formatted(tableName))
.bind("surveyResponseId", surveyResponseId)
.mapTo(clazz)
.stream()
.toList()
);
}

public List<ParticipantFile> findByEnrolleeId(UUID enrolleeId) {
return findAllByProperty("enrollee_id", enrolleeId);
}

public void deleteByEnrolleeId(UUID enrolleeId) {
deleteByProperty("enrollee_id", enrolleeId);
}

public Optional<ParticipantFile> findByEnrolleeIdAndFileName(UUID enrolleeId, String fileName) {
return findByTwoProperties("enrollee_id", enrolleeId, "file_name", fileName);
}

public List<ParticipantFile> findAllByFileNameForEnrollee(UUID enrolleeId, List<String> participantFileNames) {
return jdbi.withHandle(handle ->
handle.createQuery("""
select * from %s
where enrollee_id = :enrolleeId
and file_name in (<fileNames>)
""".formatted(tableName))
.bind("enrolleeId", enrolleeId)
.bindList("fileNames", participantFileNames)
.mapTo(clazz)
.stream()
.toList()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package bio.terra.pearl.core.dao.file;

import bio.terra.pearl.core.dao.BaseJdbiDao;
import bio.terra.pearl.core.model.survey.ParticipantFileSurveyResponse;
import org.jdbi.v3.core.Jdbi;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;

@Component
public class ParticipantFileSurveyResponseDao extends BaseJdbiDao<ParticipantFileSurveyResponse> {
public ParticipantFileSurveyResponseDao(Jdbi jdbi) {
super(jdbi);
}

@Override
protected Class<ParticipantFileSurveyResponse> getClazz() {
return ParticipantFileSurveyResponse.class;
}

public List<ParticipantFileSurveyResponse> findBySurveyResponseId(UUID surveyResponseId) {
return findAllByProperty("survey_response_id", surveyResponseId);
}

public void deleteByEnrolleeId(UUID enrolleeId) {
jdbi.withHandle(handle ->
handle.createUpdate("""
delete from participant_file_survey_response file_response
where id IN (
select inner_file_response.id from participant_file_survey_response inner_file_response
inner join participant_file inner_file on inner_file_response.participant_file_id = inner_file.id
where inner_file.enrollee_id = :enrolleeId
);
""")
.bind("enrolleeId", enrolleeId)
.execute()
);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package bio.terra.pearl.core.dao.survey;

import bio.terra.pearl.core.dao.BaseMutableJdbiDao;
import bio.terra.pearl.core.dao.fileupload.ParticipantFileDao;
import bio.terra.pearl.core.model.fileupload.ParticipantFile;
import bio.terra.pearl.core.dao.file.ParticipantFileDao;
import bio.terra.pearl.core.model.file.ParticipantFile;
import bio.terra.pearl.core.model.survey.Answer;
import bio.terra.pearl.core.model.survey.SurveyResponse;
import org.jdbi.v3.core.Jdbi;
Expand Down Expand Up @@ -41,6 +41,7 @@ public List<SurveyResponse> findByEnrolleeIdWithAnswers(UUID enrolleeId) {
Map<UUID, SurveyResponse> responseById = new HashMap<>();
for (SurveyResponse response : responses) {
responseById.put(response.getId(), response);
attachParticipantFiles(response);
}
for (Answer answer : answers) {
responseById.get(answer.getSurveyResponseId()).getAnswers().add(answer);
Expand All @@ -52,6 +53,7 @@ public Optional<SurveyResponse> findOneWithAnswers(UUID responseId) {
Optional<SurveyResponse> responseOpt = find(responseId);
responseOpt.ifPresent(response -> {
attachAnswers(response);
attachParticipantFiles(response);
});
return responseOpt;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package bio.terra.pearl.core.model.fileupload;
package bio.terra.pearl.core.model.file;

import bio.terra.pearl.core.model.BaseEntity;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bio.terra.pearl.core.model.participant;

import bio.terra.pearl.core.model.BaseEntity;
import bio.terra.pearl.core.model.file.ParticipantFile;
import bio.terra.pearl.core.model.study.StudyEnvAttached;
import bio.terra.pearl.core.model.survey.PreEnrollmentResponse;
import bio.terra.pearl.core.model.survey.SurveyResponse;
Expand Down Expand Up @@ -50,4 +51,6 @@ public class Enrollee extends BaseEntity implements StudyEnvAttached {
private List<KitRequestDto> kitRequests = new ArrayList<>();
@Builder.Default
private List<EnrolleeRelation> relations = new ArrayList<>();
@Builder.Default
private List<ParticipantFile> files = new ArrayList<>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import bio.terra.pearl.core.model.BaseEntity;
import bio.terra.pearl.core.model.audit.ResponsibleEntity;
import bio.terra.pearl.core.model.fileupload.ParticipantFile;
import bio.terra.pearl.core.model.file.ParticipantFile;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package bio.terra.pearl.core.service.fileupload;
package bio.terra.pearl.core.service.file;

import lombok.Getter;
import lombok.Setter;
Expand Down
Loading
Loading