From 812ff0f3a2d34356b2af171d7027fccd3c114dbe Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 7 Jan 2025 14:21:06 +0000 Subject: [PATCH] Stash: endpoint for create/update/delete all dv featured items at once WIP --- .../harvard/iq/dataverse/api/Dataverses.java | 68 +++++++++++++++++++ .../UpdateDataverseFeaturedItemsCommand.java | 64 +++++++++++++++++ .../iq/dataverse/api/DataversesIT.java | 23 +++++++ .../edu/harvard/iq/dataverse/api/UtilIT.java | 50 ++++++++++++-- 4 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateDataverseFeaturedItemsCommand.java diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java index 015485c9a9e..bf5aff431af 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -66,6 +66,7 @@ import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.StreamingOutput; +import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; @@ -112,6 +113,9 @@ public class Dataverses extends AbstractApiBean { @EJB PermissionServiceBean permissionService; + + @EJB + DataverseFeaturedItemServiceBean dataverseFeaturedItemServiceBean; @POST @AuthRequired @@ -1772,4 +1776,68 @@ public Response listFeaturedItems(@Context ContainerRequestContext crc, @PathPar return e.getResponse(); } } + + // TODO: Refine + @PUT + @AuthRequired + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Path("{dataverseId}/featuredItems") + public Response updateFeaturedItems( + @Context ContainerRequestContext crc, + @PathParam("dataverseId") String dvIdtf, + @FormDataParam("id") List ids, + @FormDataParam("content") List contents, + @FormDataParam("displayOrder") List displayOrders, + @FormDataParam("keepFile") List keepFiles, + @FormDataParam("file") List files) { + + try { + if (contents.size() != displayOrders.size() || displayOrders.size() != files.size()) { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Mismatch between contents, displayOrders, and files.") + .build(); + } + + Dataverse dataverse = findDataverseOrDie(dvIdtf); + List newDataverseFeaturedItemDTOs = new ArrayList<>(); + Map dataverseFeaturedItemsToUpdate = new HashMap<>(); + + for (int i = 0; i < contents.size(); i++) { + Long id = ids.get(i); + String content = contents.get(i); + Integer displayOrder = displayOrders.get(i); + boolean keepFile = keepFiles.get(i); + FormDataBodyPart fileBodyPart = files.get(i); + + InputStream fileInputStream = fileBodyPart.getValueAs(InputStream.class); + FormDataContentDisposition contentDispositionHeader = fileBodyPart.getFormDataContentDisposition(); + + if (id == 0) { + NewDataverseFeaturedItemDTO newDTO = NewDataverseFeaturedItemDTO.fromFormData(content, displayOrder, fileInputStream, contentDispositionHeader); + newDataverseFeaturedItemDTOs.add(newDTO); + } else { + DataverseFeaturedItem existingItem = dataverseFeaturedItemServiceBean.findById(id); + if (existingItem == null) { + return Response.status(Response.Status.NOT_FOUND) + // TODO + .entity("Featured item not found with ID: " + id) + .build(); + } + UpdatedDataverseFeaturedItemDTO updatedDTO = UpdatedDataverseFeaturedItemDTO.fromFormData(content, displayOrder, keepFile, fileInputStream, contentDispositionHeader); + dataverseFeaturedItemsToUpdate.put(existingItem, updatedDTO); + } + } + + execCommand(new UpdateDataverseFeaturedItemsCommand( + createDataverseRequest(getRequestUser(crc)), + dataverse, + newDataverseFeaturedItemDTOs, + dataverseFeaturedItemsToUpdate + )); + + return Response.ok().entity("Featured items updated successfully").build(); + } catch (WrappedResponse wr) { + return wr.getResponse(); + } + } } diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateDataverseFeaturedItemsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateDataverseFeaturedItemsCommand.java new file mode 100644 index 00000000000..a1132188e4b --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/UpdateDataverseFeaturedItemsCommand.java @@ -0,0 +1,64 @@ +package edu.harvard.iq.dataverse.engine.command.impl; + +import edu.harvard.iq.dataverse.Dataverse; +import edu.harvard.iq.dataverse.DataverseFeaturedItem; +import edu.harvard.iq.dataverse.api.dto.NewDataverseFeaturedItemDTO; +import edu.harvard.iq.dataverse.api.dto.UpdatedDataverseFeaturedItemDTO; +import edu.harvard.iq.dataverse.authorization.Permission; +import edu.harvard.iq.dataverse.engine.command.AbstractVoidCommand; +import edu.harvard.iq.dataverse.engine.command.CommandContext; +import edu.harvard.iq.dataverse.engine.command.DataverseRequest; +import edu.harvard.iq.dataverse.engine.command.RequiredPermissions; +import edu.harvard.iq.dataverse.engine.command.exception.CommandException; + +import java.util.List; +import java.util.Map; + +@RequiredPermissions({Permission.EditDataverse}) +public class UpdateDataverseFeaturedItemsCommand extends AbstractVoidCommand { + + private final Dataverse dataverse; + private final List newDataverseFeaturedItemDTOs; + private final Map dataverseFeaturedItemsToUpdate; + + public UpdateDataverseFeaturedItemsCommand(DataverseRequest request, + Dataverse dataverse, + List newDataverseFeaturedItemDTOs, + Map dataverseFeaturedItemsToUpdate) { + super(request, dataverse); + this.dataverse = dataverse; + this.newDataverseFeaturedItemDTOs = newDataverseFeaturedItemDTOs; + this.dataverseFeaturedItemsToUpdate = dataverseFeaturedItemsToUpdate; + } + + @Override + protected void executeImpl(CommandContext ctxt) throws CommandException { + updateOrDeleteExistingFeaturedItems(ctxt); + createNewFeaturedItems(ctxt); + } + + private void updateOrDeleteExistingFeaturedItems(CommandContext ctxt) throws CommandException { + List featuredItemsToDelete = dataverse.getDataverseFeaturedItems(); + + for (Map.Entry entry : dataverseFeaturedItemsToUpdate.entrySet()) { + DataverseFeaturedItem featuredItem = entry.getKey(); + UpdatedDataverseFeaturedItemDTO updatedDTO = entry.getValue(); + + if (featuredItemsToDelete.contains(featuredItem)) { + featuredItemsToDelete.remove(featuredItem); + } + + ctxt.engine().submit(new UpdateDataverseFeaturedItemCommand(getRequest(), featuredItem, updatedDTO)); + } + + for (DataverseFeaturedItem featuredItem : featuredItemsToDelete) { + ctxt.engine().submit(new DeleteDataverseFeaturedItemCommand(getRequest(), featuredItem)); + } + } + + private void createNewFeaturedItems(CommandContext ctxt) throws CommandException { + for (NewDataverseFeaturedItemDTO dto : newDataverseFeaturedItemDTOs) { + ctxt.engine().submit(new CreateDataverseFeaturedItemCommand(getRequest(), dataverse, dto)); + } + } +} diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java index 8153f7ffe91..51876e13a72 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java @@ -1621,4 +1621,27 @@ public void testCreateFeaturedItem() { .body("message", equalTo("Can't find dataverse with identifier='thisDataverseDoesNotExist'")) .statusCode(NOT_FOUND.getStatusCode()); } + + // TODO: Complete + @Test + public void testUpdateFeaturedItems() { + Response createUserResponse = UtilIT.createRandomUser(); + String apiToken = UtilIT.getApiTokenFromResponse(createUserResponse); + Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); + createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); + String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); + + List contents = Arrays.asList("Content 1", "Content 2"); + List orders = Arrays.asList(1, 2); + List pathsToFiles = Arrays.asList("src/test/resources/images/coffeeshop.png", "src/test/resources/images/coffeeshop.png"); + + Response updateDataverseFeaturedItemsResponse = UtilIT.updateDataverseFeaturedItems(dataverseAlias, contents, orders, pathsToFiles, apiToken); + updateDataverseFeaturedItemsResponse.prettyPrint(); + updateDataverseFeaturedItemsResponse.then().assertThat() + .statusCode(OK.getStatusCode()); + + Response listFeaturedItemsResponse = UtilIT.listDataverseFeaturedItems(dataverseAlias, apiToken); + listFeaturedItemsResponse.prettyPrint(); + + } } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java index 40df922b4fc..2f0417e767d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java @@ -1,12 +1,14 @@ package edu.harvard.iq.dataverse.api; +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.harvard.iq.dataverse.api.dto.NewDataverseFeaturedItemDTO; import edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder; import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; import io.restassured.response.Response; import java.io.*; -import java.util.UUID; +import java.util.*; import java.util.logging.Logger; import jakarta.json.Json; import jakarta.json.JsonObjectBuilder; @@ -29,15 +31,13 @@ import org.junit.jupiter.api.Test; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import io.restassured.specification.RequestSpecification; -import java.util.List; import com.mashape.unirest.http.Unirest; import com.mashape.unirest.http.exceptions.UnirestException; import com.mashape.unirest.request.GetRequest; import edu.harvard.iq.dataverse.util.FileUtil; -import java.util.Base64; import org.apache.commons.io.IOUtils; import java.nio.file.Path; -import java.util.ArrayList; + import org.apache.commons.lang3.math.NumberUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -52,8 +52,7 @@ import edu.harvard.iq.dataverse.settings.FeatureFlags; import edu.harvard.iq.dataverse.util.StringUtil; -import java.util.Collections; - +import static org.apache.http.entity.ContentType.APPLICATION_JSON; import static org.junit.jupiter.api.Assertions.*; public class UtilIT { @@ -4414,4 +4413,43 @@ static Response listDataverseFeaturedItems(String dataverseAlias, String apiToke .contentType("application/json") .get("/api/dataverses/" + dataverseAlias + "/featuredItems"); } + + + // TODO: Refine + static Response updateDataverseFeaturedItems( + String dataverseAlias, + List contents, + List orders, + List pathsToFiles, + String apiToken) { + + RequestSpecification requestSpecification = given() + .header(API_TOKEN_HTTP_HEADER, apiToken) + .contentType(ContentType.MULTIPART); + + int size = contents.size(); + if (orders.size() != size || pathsToFiles.size() != size) { + throw new IllegalArgumentException("Contents, orders, and pathsToFiles must have the same size."); + } + + for (int i = 0; i < size; i++) { + String content = contents.get(i); + Integer order = orders.get(i); + + requestSpecification.multiPart("content", content); + requestSpecification.multiPart("displayOrder", order); + requestSpecification.multiPart("keepFile", false); + requestSpecification.multiPart("id", 0); + + String pathToFile = pathsToFiles.get(i); + if (pathToFile != null && !pathToFile.isEmpty()) { + requestSpecification.multiPart("file", new File(pathToFile)); + } + } + + return requestSpecification + .when() + .put("/api/dataverses/" + dataverseAlias + "/featuredItems"); + } + }