Skip to content

Commit

Permalink
Changed: multiple featured item creation endpoint converted to single
Browse files Browse the repository at this point in the history
  • Loading branch information
GPortas committed Dec 24, 2024
1 parent 755fd4d commit 67e3fe0
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 13 deletions.
57 changes: 53 additions & 4 deletions src/main/java/edu/harvard/iq/dataverse/DataverseFeaturedItem.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,71 @@
package edu.harvard.iq.dataverse;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;

@Entity
@Table(indexes = {@Index(columnList = "dataverse_id"), @Index(columnList = "displayOrder")})
public class DataverseFeaturedItem {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
@JoinColumn(name = "dataverse_id", nullable = false)
private Dataverse dataverse;

@NotBlank
@Size(max = 15000)
@Lob
@Column(columnDefinition = "TEXT", nullable = false)
private String content;

@Min(0)
@Column(nullable = false)
private int displayOrder;

private String imageFileName;

public void setId(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public Dataverse getDataverse() {
return dataverse;
}

public void setDataverse(Dataverse dataverse) {
this.dataverse = dataverse;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public int getDisplayOrder() {
return displayOrder;
}

public void setDisplayOrder(int displayOrder) {
this.displayOrder = displayOrder;
}

public String getImageFileName() {
return imageFileName;
}

public void setImageFileName(String imageFileName) {
this.imageFileName = imageFileName;
}
}
29 changes: 29 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -1781,4 +1781,33 @@ private File createTempDir(Dataverse editDv) {
throw new RuntimeException("Error creating temp directory", e); // improve error handling
}
}

@POST
@AuthRequired
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("{identifier}/featuredItem")
public Response createFeaturedItem(@Context ContainerRequestContext crc,
@PathParam("identifier") String dvIdtf,
@FormDataParam("content") String content,
@FormDataParam("order") int order,
@FormDataParam("file") InputStream fileInputStream,
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader) {
Dataverse dataverse;
try {
dataverse = findDataverseOrDie(dvIdtf);
} catch (WrappedResponse wr) {
return wr.getResponse();
}
NewDataverseFeaturedItemDTO newDataverseFeaturedItemDTO = NewDataverseFeaturedItemDTO.fromFormData(content, order, fileInputStream, contentDispositionHeader);
try {
DataverseFeaturedItem dataverseFeaturedItem = execCommand(new CreateDataverseFeaturedItemCommand(
createDataverseRequest(getRequestUser(crc)),
dataverse,
newDataverseFeaturedItemDTO
));
return ok(json(dataverseFeaturedItem));
} catch (WrappedResponse e) {
return e.getResponse();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,105 @@

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.engine.command.AbstractCommand;
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 edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.util.BundleUtil;
import edu.harvard.iq.dataverse.util.FileUtil;
import org.apache.tika.Tika;

/**
* A command that creates a featured item in a {@link Dataverse}.
*/
//TODO permissions
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;

// TODO: Permissions
@RequiredPermissions({})
public class CreateDataverseFeaturedItemCommand extends AbstractCommand<DataverseFeaturedItem> {

private final Dataverse dataverse;
private final NewDataverseFeaturedItemDTO newDataverseFeaturedItemDTO;

public CreateDataverseFeaturedItemCommand(DataverseRequest request, Dataverse dataverse) {
public CreateDataverseFeaturedItemCommand(DataverseRequest request, Dataverse dataverse, NewDataverseFeaturedItemDTO newDataverseFeaturedItemDTO) {
super(request, dataverse);
this.dataverse = dataverse;
this.newDataverseFeaturedItemDTO = newDataverseFeaturedItemDTO;
}

@Override
public DataverseFeaturedItem execute(CommandContext ctxt) throws CommandException {
return null;
DataverseFeaturedItem featuredItem = new DataverseFeaturedItem();
setImageIfAvailable(featuredItem);
featuredItem.setContent(newDataverseFeaturedItemDTO.getContent());
featuredItem.setDisplayOrder(newDataverseFeaturedItemDTO.getDisplayOrder());
featuredItem.setDataverse(dataverse);
return featuredItem;
}

private void setImageIfAvailable(DataverseFeaturedItem featuredItem) throws IllegalCommandException {
if (newDataverseFeaturedItemDTO.getImageFileName() != null) {
try {
prepareUploadedImageFile();
} catch (IOException e) {
throw new RuntimeException(BundleUtil.getStringFromBundle("dataverse.create.featuredItem.error.imageFileProcessing", List.of(e.getMessage())), e);
}
featuredItem.setImageFileName(newDataverseFeaturedItemDTO.getImageFileName());
}
}

private void prepareUploadedImageFile() throws IOException, IllegalCommandException {
// Step 1: Create a temporary directory to store the uploaded image
Path tempDir = createTempDir();
File uploadedFile = new File(tempDir.toFile(), newDataverseFeaturedItemDTO.getImageFileName());

if (!uploadedFile.exists()) {
uploadedFile.createNewFile();
}

// Step 2: Convert the InputStream into a temporary file for validation
File tempFile = FileUtil.inputStreamToFile(newDataverseFeaturedItemDTO.getFileInputStream());

// Step 3: Validate the uploaded file (type and size)
validateFile(tempFile);

// Step 4: Copy the validated file to the final destination
Files.copy(tempFile.toPath(), uploadedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}

private Path createTempDir() throws IOException {
Path tempRoot = Path.of(JvmSettings.DOCROOT_DIRECTORY.lookup(), JvmSettings.FEATURED_ITEMS_IMAGE_UPLOADS_DIRECTORY.lookup(), dataverse.getId().toString(), String.valueOf(newDataverseFeaturedItemDTO.getDisplayOrder()));
Files.createDirectories(tempRoot);
return tempRoot;
}

private void validateFile(File file) throws IOException, IllegalCommandException {
validateFileType(file);
validateFileSize(file);
}

private void validateFileType(File file) throws IOException, IllegalCommandException {
Tika tika = new Tika();
String mimeType = tika.detect(file);
boolean isImageFile = mimeType != null && mimeType.startsWith("image/");
if (!isImageFile) {
throw new IllegalCommandException(
BundleUtil.getStringFromBundle("dataverse.create.featuredItem.error.invalidFileType"),
this
);
}
}

private void validateFileSize(File file) throws IllegalCommandException {
Integer featuredItemsImageMaxSize = JvmSettings.FEATURED_ITEMS_IMAGE_MAXSIZE.lookup(Integer.class);
if (file.length() > featuredItemsImageMaxSize) {
throw new IllegalCommandException(BundleUtil.getStringFromBundle("dataverse.create.featuredItem.error.fileSizeExceedsLimit", List.of(featuredItemsImageMaxSize.toString())), this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public enum JvmSettings {
GUESTBOOK_AT_REQUEST(SCOPE_FILES, "guestbook-at-request"),
GLOBUS_CACHE_MAXAGE(SCOPE_FILES, "globus-cache-maxage"),
GLOBUS_TASK_MONITORING_SERVER(SCOPE_FILES, "globus-monitoring-server"),
SCOPE_FEATURED_ITEMS(SCOPE_FILES, "featured-items"),
FEATURED_ITEMS_IMAGE_MAXSIZE(SCOPE_FEATURED_ITEMS, "image-maxsize"),
FEATURED_ITEMS_IMAGE_UPLOADS_DIRECTORY(SCOPE_FEATURED_ITEMS, "image-uploads"),

//STORAGE DRIVER SETTINGS
SCOPE_DRIVER(SCOPE_FILES),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1417,4 +1417,12 @@ private static JsonObjectBuilder jsonDataverseInputLevel(DataverseFieldTypeInput
jsonObjectBuilder.add("include", inputLevel.isInclude());
return jsonObjectBuilder;
}

public static JsonObjectBuilder json(DataverseFeaturedItem dataverseFeaturedItem) {
return jsonObjectBuilder()
.add("id", dataverseFeaturedItem.getId())
.add("content", dataverseFeaturedItem.getContent())
.add("imageFileName", dataverseFeaturedItem.getImageFileName())
.add("displayOrder", dataverseFeaturedItem.getDisplayOrder());
}
}
3 changes: 3 additions & 0 deletions src/main/java/propertyFiles/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,9 @@ dataverse.facets.error.fieldtypenotfacetable=Dataset field type '{0}' is not fac
dataverse.metadatablocks.error.invalidmetadatablockname=Invalid metadata block name: {0}
dataverse.create.error.jsonparse=Error parsing Json: {0}
dataverse.create.error.jsonparsetodataverse=Error parsing the POSTed json into a dataverse: {0}
dataverse.create.featuredItem.error.imageFileProcessing=Error processing featured item file: {0}
dataverse.create.featuredItem.error.fileSizeExceedsLimit=File exceeds the maximum size of {0}
dataverse.create.featuredItem.error.invalidFileType=Invalid image file type
# rolesAndPermissionsFragment.xhtml

# advanced.xhtml
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/microprofile-config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ dataverse.files.directory=${STORAGE_DIR:/tmp/dataverse}
dataverse.files.uploads=${STORAGE_DIR:${com.sun.aas.instanceRoot}}/uploads
dataverse.files.docroot=${STORAGE_DIR:${com.sun.aas.instanceRoot}}/docroot
dataverse.files.globus-cache-maxage=5
dataverse.files.featured-items.image-maxsize=1000000
dataverse.files.featured-items.image-uploads=featuredItems

# SEARCH INDEX
dataverse.solr.host=localhost
Expand Down
16 changes: 15 additions & 1 deletion src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -1568,8 +1568,22 @@ public void testUpdateFeaturedItems() {
createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode());
String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse);

// Should not return any error when passing correct file and data
String pathToTestFile = "src/test/resources/images/coffeeshop.png";
Response updateFeatureItemsResponse = UtilIT.updateFeaturedItems(dataverseAlias, apiToken, "test", "test", pathToTestFile);
Response updateFeatureItemsResponse = UtilIT.createFeaturedItem(dataverseAlias, apiToken, "test", "test", pathToTestFile);
updateFeatureItemsResponse.then().assertThat()
.body("data.content", equalTo("test"))
.body("data.imageFileName", equalTo("coffeeshop.png"))
.body("data.displayOrder", equalTo(0))
.statusCode(OK.getStatusCode());

// Should return error when passing incorrect file type
pathToTestFile = "src/test/resources/tab/test.tab";
updateFeatureItemsResponse = UtilIT.createFeaturedItem(dataverseAlias, apiToken, "test", "test", pathToTestFile);
updateFeatureItemsResponse.prettyPrint();
updateFeatureItemsResponse.then().assertThat()
.body("message", equalTo(BundleUtil.getStringFromBundle("dataverse.create.featuredItem.error.invalidFileType")))
// FIXME
.statusCode(BAD_REQUEST.getStatusCode());
}
}
4 changes: 2 additions & 2 deletions src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -4305,14 +4305,14 @@ static Response deleteDatasetTypes(long doomed, String apiToken) {
.delete("/api/datasets/datasetTypes/" + doomed);
}

static Response updateFeaturedItems(String dataverseAlias, String apiToken, String title, String content, String pathToFile) {
static Response createFeaturedItem(String dataverseAlias, String apiToken, String title, String content, String pathToFile) {
return given()
.header(API_TOKEN_HTTP_HEADER, apiToken)
.contentType(ContentType.MULTIPART)
.multiPart("title", title)
.multiPart("content", content)
.multiPart("file", new File(pathToFile))
.when()
.put("/api/dataverses/" + dataverseAlias + "/featuredItems");
.post("/api/dataverses/" + dataverseAlias + "/featuredItem");
}
}

0 comments on commit 67e3fe0

Please sign in to comment.