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
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,11 @@ DarCollectionService providesDarCollectionService() {
@Provides
FileStorageObjectService providesFileStorageObjectService() {
return new FileStorageObjectService(
providesFileStorageObjectDAO(), providesGCSService(), providesDatasetService());
providesFileStorageObjectDAO(),
providesGCSService(),
providesDatasetService(),
providesDacService(),
providesDataAccessRequestService());
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ Integer insertNewFile(
@Bind("createUserId") Integer createUserId,
@Bind("createDate") Instant createDate);

// Preferred create contract for new document upload paths.
default Integer create(
String fileName,
String category,
String gcsFileUri,
String mediaType,
String entityId,
Integer createUserId,
Instant createDate) {
return insertNewFile(
fileName, category, gcsFileUri, mediaType, entityId, createUserId, createDate);
}

@SqlUpdate(
"""
UPDATE file_storage_object
Expand Down Expand Up @@ -78,6 +91,37 @@ void deleteFilesByEntityId(
@Bind("deleteUserId") Integer deleteUserId,
@Bind("deleteDate") Instant deleteDate);

@SqlUpdate(
"""
UPDATE file_storage_object
SET deleted=true,
delete_user_id=:deleteUserId,
delete_date=:deleteDate
WHERE entity_id = :entityId
AND file_storage_object_id = :fileStorageObjectId
AND (deleted = false OR deleted IS NULL)
""")
void softDelete(
@Bind("entityId") String entityId,
@Bind("fileStorageObjectId") Integer fileStorageObjectId,
@Bind("deleteUserId") Integer deleteUserId,
@Bind("deleteDate") Instant deleteDate);

@SqlUpdate(
"""
UPDATE file_storage_object
SET category=:category,
update_user_id=:updateUserId,
update_date=:updateDate
WHERE file_storage_object_id = :fileStorageObjectId
AND (deleted = false OR deleted IS NULL)
""")
void updateCategory(
@Bind("fileStorageObjectId") Integer fileStorageObjectId,
@Bind("category") String category,
@Bind("updateUserId") Integer updateUserId,
@Bind("updateDate") Instant updateDate);

@SqlQuery(
"""
SELECT *
Expand All @@ -86,6 +130,10 @@ void deleteFilesByEntityId(
""")
FileStorageObject findFileById(@Bind("fileStorageObjectId") Integer fileStorageObjectId);

default FileStorageObject findById(Integer fileStorageObjectId) {
return findFileById(fileStorageObjectId);
}

@RegisterRowMapper(FileStorageObjectMapperWithFSOPrefix.class)
@SqlQuery(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.Optional;

public enum DocumentEntity {
DAC("dac"),
DAR("dar"),
DATASET("dataset"),
STUDY("study");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.broadinstitute.consent.http.enumeration;

/**
* Represents the type of operation being performed on a FileStorageObject. Used by
* FileStorageObjectService.checkAccess(...) to determine which authorization rules apply.
*/
public enum OperationType {
/** Read-only operations: listing documents, fetching metadata, downloading files. */
READ,

/** Create / update / delete operations. */
CRUD
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.broadinstitute.consent.http.models;

public class FileStorageObjectCategoryUpdateRequest {

private String category;

public String getCategory() {
return category;
}

public void setCategory(String category) {
this.category = category;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,29 @@
import com.google.inject.Inject;
import io.dropwizard.auth.Auth;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.StreamingOutput;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.List;
import org.broadinstitute.consent.http.models.DuosUser;
import org.broadinstitute.consent.http.models.FileStorageObject;
import org.broadinstitute.consent.http.models.FileStorageObjectCategoryUpdateRequest;
import org.broadinstitute.consent.http.models.User;
import org.broadinstitute.consent.http.service.FileStorageObjectService;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;

@Path("api/{entity}")
public class DocumentResource extends Resource {
Expand All @@ -36,8 +48,7 @@ public Response findDocumentsByEntity(
try {
User user = duosUser.getUser();
List<FileStorageObject> fileStorageObjects =
fileStorageObjectService.fetchAllMetadataByEntityAndEntityIdForRead(
user, entity, entityId);
fileStorageObjectService.listDocuments(user, entity, entityId);
return Response.ok(fileStorageObjects).build();
} catch (Exception e) {
return createExceptionResponse(e);
Expand All @@ -56,11 +67,112 @@ public Response findDocumentByEntity(
try {
User user = duosUser.getUser();
FileStorageObject fileStorageObject =
fileStorageObjectService.fetchMetadataByEntityAndEntityIdForRead(
user, entity, entityId, id);
fileStorageObjectService.getDocument(user, entity, entityId, id);
return Response.ok(fileStorageObject).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}

@DELETE
@Path("/{entityId}/document/{id}")
@Produces(MediaType.APPLICATION_JSON)
@PermitAll
public Response deleteDocumentByEntity(
@Auth DuosUser duosUser,
@PathParam("entity") String entity,
@PathParam("entityId") String entityId,
@PathParam("id") Integer id) {
try {
User user = duosUser.getUser();
FileStorageObject deleted =
fileStorageObjectService.deleteDocument(user, entity, entityId, id);
return Response.ok(deleted).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}

@PUT
@Path("/{entityId}/document/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@PermitAll
public Response updateDocumentCategoryByEntity(
@Auth DuosUser duosUser,
@PathParam("entity") String entity,
@PathParam("entityId") String entityId,
@PathParam("id") Integer id,
FileStorageObjectCategoryUpdateRequest request) {
try {
User user = duosUser.getUser();
String category = request == null ? null : request.getCategory();
FileStorageObject updated =
fileStorageObjectService.updateDocumentCategory(user, entity, entityId, id, category);
return Response.ok(updated).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}

@POST
@Path("{entityId}/document")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@PermitAll
public Response uploadDocument(
@Auth DuosUser duosUser,
@PathParam("entity") String entity,
@PathParam("entityId") String entityId,
@FormDataParam("file") InputStream file,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("category") String categoryStr) {
try {
validateFileDetails(fileDetail);
User user = duosUser.getUser();
FileStorageObject created =
fileStorageObjectService.uploadDocument(
user, entity, entityId, file, fileDetail, categoryStr);
return Response.status(Response.Status.CREATED).entity(created).build();
} catch (Exception e) {
return createExceptionResponse(e);
}
}

@GET
@Path("/{entityId}/document/{id}/file")
@PermitAll
public Response findDocumentFileByEntity(
@Auth DuosUser duosUser,
@PathParam("entity") String entity,
@PathParam("entityId") String entityId,
@PathParam("id") Integer id) {
try {
User user = duosUser.getUser();
FileStorageObject fileStorageObject =
fileStorageObjectService.getDocumentFile(user, entity, entityId, id);
InputStream stream = fileStorageObject.getUploadedFile();
StreamingOutput streamingOutput =
output -> {
try (InputStream input = stream) {
input.transferTo(output);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
return Response.ok(streamingOutput)
.type(fileStorageObject.getMediaType())
.header(
"Content-Disposition",
"attachment; filename=\"" + fileStorageObject.getFileName() + "\"")
.build();
} catch (WebApplicationException e) {
if (e.getResponse().getStatus() == Response.Status.BAD_GATEWAY.getStatusCode()) {
return Response.status(Response.Status.BAD_GATEWAY).build();
}
return createExceptionResponse(e);
} catch (Exception e) {
return createExceptionResponse(e);
}
}
}
Loading
Loading