Skip to content

Commit 2fb4a0d

Browse files
authored
Merge pull request #11499 from IQSS/11414-add-dvobject-type-to-featured-items
adding type and dvObject to dataverse featured items
2 parents 5db10ea + 0b14812 commit 2fb4a0d

28 files changed

+746
-151
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
### Feature: Added dvObject and type fields to Featured Items
2+
3+
Dataverse Featured Items can now be linked to Dataverses, Datasets, or Datafiles.
4+
5+
Pre-existing featured items as well as new items without dvObjects will be defaulted to type=custom.
6+
7+
Featured Items with dvObjects will be filtered out of lists if the dvObject should not be viewed (i.e. datafiles that are restricted or datasets that are deaccessioned)

doc/sphinx-guides/source/api/native-api.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,9 +1218,13 @@ The ``file`` parameter must be specified for each image we want to attach to fea
12181218

12191219
The ``id`` parameter must be ``0`` for new items or set to the item's identifier for updates. The ``fileName`` parameter should be empty to exclude an image or match the name of a file sent in a ``file`` parameter to set a new image. ``keepFile`` must always be set to ``false``, unless it's an update to a featured item where we want to preserve the existing image, if one exists.
12201220

1221+
The ``type`` and ``dvObject`` parameters are optional. These allow you to link the featured item to a Dataverse, Dataset, or Datafile.
1222+
The ``dvObject`` can be passed as the id or the persistent identifier and the ``type`` must be passed as either "dataverse", "dataset", or "datafile", depending on the type of object.
1223+
If no ``dvObject`` is passed the ``type`` will default to "custom" designating no linked object.
1224+
12211225
Note that any existing featured item not included in the call with its associated identifier and corresponding properties will be removed from the collection.
12221226

1223-
The following example creates two featured items, with an image assigned to the second one:
1227+
The following example creates two featured items, with an image and a dataset assigned to the second one:
12241228

12251229
.. code-block:: bash
12261230
@@ -1234,6 +1238,8 @@ The following example creates two featured items, with an image assigned to the
12341238
export SECOND_ITEM_IMAGE_FILENAME='image.png'
12351239
export SECOND_ITEM_CONTENT='Content 2'
12361240
export SECOND_ITEM_DISPLAY_ORDER=2
1241+
export SECOND_ITEM_TYPE="dataset"
1242+
export SECOND_ITEM_DVOBJECT="doi:ZZ7/MOSEISLEYDB94"
12371243
12381244
curl -H "X-Dataverse-key:$API_TOKEN" \
12391245
-X PUT \
@@ -1243,6 +1249,8 @@ The following example creates two featured items, with an image assigned to the
12431249
-F "fileName=" -F "fileName=$SECOND_ITEM_IMAGE_FILENAME" \
12441250
-F "keepFile=false" -F "keepFile=false" \
12451251
-F "file=@$SECOND_ITEM_IMAGE_FILENAME" \
1252+
-F "type=" -F "type=@$SECOND_ITEM_TYPE" \
1253+
-F "dvObject=" -F "dvObject=@$SECOND_ITEM_DVOBJECT" \
12461254
"$SERVER_URL/api/dataverses/$ID/featuredItems"
12471255
12481256
@@ -1258,9 +1266,11 @@ The fully expanded example above (without environment variables) looks like this
12581266
-F "fileName=" -F "fileName=image.png" \
12591267
-F "keepFile=false" -F "keepFile=false" \
12601268
1269+
-F "type=" -F "type=dataset" \
1270+
-F "dvObject=" -F "dvObject=doi:ZZ7/MOSEISLEYDB94" \
12611271
"https://demo.dataverse.org/api/dataverses/root/featuredItems"
12621272
1263-
The following example creates one featured item and updates a second one, keeping the existing image it may have had:
1273+
The following example creates one featured item and updates a second one, keeping the existing image it may have had but removes the dataset link and defaults the type to "custom":
12641274

12651275
.. code-block:: bash
12661276

src/main/java/edu/harvard/iq/dataverse/Dataverse.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -353,17 +353,6 @@ public void setMetadataBlockFacets(List<DataverseMetadataBlockFacet> metadataBlo
353353
this.metadataBlockFacets = metadataBlockFacets;
354354
}
355355

356-
@OneToMany(mappedBy = "dataverse")
357-
private List<DataverseFeaturedItem> dataverseFeaturedItems = new ArrayList<>();
358-
359-
public List<DataverseFeaturedItem> getDataverseFeaturedItems() {
360-
return this.dataverseFeaturedItems;
361-
}
362-
363-
public void setDataverseFeaturedItems(List<DataverseFeaturedItem> dataverseFeaturedItems) {
364-
this.dataverseFeaturedItems = dataverseFeaturedItems;
365-
}
366-
367356
public List<Guestbook> getParentGuestbooks() {
368357
List<Guestbook> retList = new ArrayList<>();
369358
Dataverse testDV = this;

src/main/java/edu/harvard/iq/dataverse/DvObject.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package edu.harvard.iq.dataverse;
22

33
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
4+
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
45
import edu.harvard.iq.dataverse.pidproviders.PidUtil;
56
import edu.harvard.iq.dataverse.storageuse.StorageQuota;
67

78
import java.sql.Timestamp;
89
import java.text.SimpleDateFormat;
9-
import java.util.Date;
10-
import java.util.List;
11-
import java.util.Objects;
12-
import java.util.Set;
10+
import java.util.*;
1311
import java.util.logging.Logger;
1412

1513
import jakarta.persistence.*;
@@ -141,7 +139,17 @@ public String visit(DataFile df) {
141139
private String storageIdentifier;
142140

143141
@Column(insertable = false, updatable = false) private String dtype;
144-
142+
143+
@OneToMany(mappedBy="dvobject",fetch = FetchType.LAZY,cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
144+
private List<DataverseFeaturedItem> dataverseFeaturedItems;
145+
146+
public List<DataverseFeaturedItem> getDataverseFeaturedItems() {
147+
return this.dataverseFeaturedItems;
148+
}
149+
public void setDataverseFeaturedItems(List<DataverseFeaturedItem> dataverseFeaturedItems) {
150+
this.dataverseFeaturedItems = dataverseFeaturedItems;
151+
}
152+
145153
/*
146154
* Add PID related fields
147155
*/

src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import edu.harvard.iq.dataverse.confirmemail.ConfirmEmailServiceBean;
1313
import edu.harvard.iq.dataverse.datacapturemodule.DataCaptureModuleServiceBean;
1414
import edu.harvard.iq.dataverse.dataset.DatasetTypeServiceBean;
15+
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
1516
import edu.harvard.iq.dataverse.engine.command.Command;
1617
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
1718
import edu.harvard.iq.dataverse.engine.command.exception.*;
@@ -377,11 +378,21 @@ protected Dataset findDatasetOrDie(String id) throws WrappedResponse {
377378
protected Dataset findDatasetOrDie(String id, boolean deep) throws WrappedResponse {
378379
Long datasetId;
379380
Dataset dataset;
380-
if (id.equals(PERSISTENT_ID_KEY)) {
381-
String persistentId = getRequestParameter(PERSISTENT_ID_KEY.substring(1));
382-
if (persistentId == null) {
381+
if (isNumeric(id)) {
382+
try {
383+
datasetId = Long.parseLong(id);
384+
} catch (NumberFormatException nfe) {
383385
throw new WrappedResponse(
384-
badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", Collections.singletonList(PERSISTENT_ID_KEY.substring(1)))));
386+
badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.bad.id", Collections.singletonList(id))));
387+
}
388+
} else {
389+
String persistentId = id;
390+
if (id.equals(PERSISTENT_ID_KEY)) {
391+
persistentId = getRequestParameter(PERSISTENT_ID_KEY.substring(1));
392+
if (persistentId == null) {
393+
throw new WrappedResponse(
394+
badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", Collections.singletonList(PERSISTENT_ID_KEY.substring(1)))));
395+
}
385396
}
386397
GlobalId globalId;
387398
try {
@@ -398,13 +409,6 @@ protected Dataset findDatasetOrDie(String id, boolean deep) throws WrappedRespon
398409
throw new WrappedResponse(
399410
notFound(BundleUtil.getStringFromBundle("find.dataset.error.dataset_id_is_null", Collections.singletonList(PERSISTENT_ID_KEY.substring(1)))));
400411
}
401-
} else {
402-
try {
403-
datasetId = Long.parseLong(id);
404-
} catch (NumberFormatException nfe) {
405-
throw new WrappedResponse(
406-
badRequest(BundleUtil.getStringFromBundle("find.dataset.error.dataset.not.found.bad.id", Collections.singletonList(id))));
407-
}
408412
}
409413
if (deep) {
410414
dataset = datasetSvc.findDeep(datasetId);
@@ -575,6 +579,47 @@ protected DvObject findDvo(@NotNull final String id) throws WrappedResponse {
575579
return d;
576580
}
577581

582+
/**
583+
*
584+
* @param dvIdtf
585+
* @param type
586+
* @return DvObject if type matches or throw exception
587+
* @throws WrappedResponse
588+
*/
589+
@NotNull
590+
protected DvObject findDvoByIdAndFeaturedItemTypeOrDie(@NotNull final String dvIdtf, String type) throws WrappedResponse {
591+
try {
592+
DataverseFeaturedItem.TYPES dvType = DataverseFeaturedItem.getDvType(type);
593+
DvObject dvObject = isNumeric(dvIdtf) ? findDvo(Long.valueOf(dvIdtf)) : null;
594+
if (dvObject == null) {
595+
List<DataverseFeaturedItem.TYPES> types = new ArrayList<>();
596+
types.addAll(List.of(DataverseFeaturedItem.TYPES.values()));
597+
types.remove(dvType);
598+
types.add(0, dvType); // put the requested type first for speed of lookup
599+
for (DataverseFeaturedItem.TYPES t : types) {
600+
try {
601+
if (DataverseFeaturedItem.TYPES.DATAVERSE == t) {
602+
dvObject = findDataverseOrDie(dvIdtf);
603+
break;
604+
} else if (DataverseFeaturedItem.TYPES.DATASET == t) {
605+
dvObject = findDatasetOrDie(dvIdtf);
606+
break;
607+
} else if (DataverseFeaturedItem.TYPES.DATAFILE == t) {
608+
dvObject = findDataFileOrDie(dvIdtf);
609+
break;
610+
}
611+
} catch (WrappedResponse e) {
612+
// ignore errors to allow other find*OrDie to be called
613+
}
614+
}
615+
}
616+
DataverseFeaturedItem.validateTypeAndDvObject(dvIdtf, dvObject, dvType);
617+
return dvObject;
618+
} catch (IllegalArgumentException e) {
619+
throw new WrappedResponse(error(Response.Status.BAD_REQUEST, e.getMessage()));
620+
}
621+
}
622+
578623
protected <T> T failIfNull( T t, String errorMessage ) throws WrappedResponse {
579624
if ( t != null ) return t;
580625
throw new WrappedResponse( error( Response.Status.BAD_REQUEST,errorMessage) );

src/main/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItems.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package edu.harvard.iq.dataverse.api;
22

3+
import edu.harvard.iq.dataverse.DvObject;
34
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
45
import edu.harvard.iq.dataverse.api.dto.UpdatedDataverseFeaturedItemDTO;
56
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
@@ -51,6 +52,8 @@ public Response deleteFeaturedItem(@Context ContainerRequestContext crc, @PathPa
5152
public Response updateFeaturedItem(@Context ContainerRequestContext crc,
5253
@PathParam("id") Long id,
5354
@FormDataParam("content") String content,
55+
@FormDataParam("type") String type,
56+
@FormDataParam("dvObject") String dvObjectIdtf,
5457
@FormDataParam("displayOrder") int displayOrder,
5558
@FormDataParam("keepFile") boolean keepFile,
5659
@FormDataParam("file") InputStream imageFileInputStream,
@@ -60,7 +63,8 @@ public Response updateFeaturedItem(@Context ContainerRequestContext crc,
6063
if (dataverseFeaturedItem == null) {
6164
throw new WrappedResponse(error(Response.Status.NOT_FOUND, MessageFormat.format(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notFound"), id)));
6265
}
63-
UpdatedDataverseFeaturedItemDTO updatedDataverseFeaturedItemDTO = UpdatedDataverseFeaturedItemDTO.fromFormData(content, displayOrder, keepFile, imageFileInputStream, contentDispositionHeader);
66+
DvObject dvObject = (dvObjectIdtf != null) ? findDvoByIdAndFeaturedItemTypeOrDie(dvObjectIdtf, type) : null;
67+
UpdatedDataverseFeaturedItemDTO updatedDataverseFeaturedItemDTO = UpdatedDataverseFeaturedItemDTO.fromFormData(content, displayOrder, keepFile, imageFileInputStream, contentDispositionHeader, type, dvObject);
6468
return ok(json(execCommand(new UpdateDataverseFeaturedItemCommand(createDataverseRequest(getRequestUser(crc)), dataverseFeaturedItem, updatedDataverseFeaturedItemDTO))));
6569
} catch (WrappedResponse e) {
6670
return e.getResponse();

src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,18 +1792,24 @@ public Response getUserPermissionsOnDataverse(@Context ContainerRequestContext c
17921792
@Path("{identifier}/featuredItems")
17931793
public Response createFeaturedItem(@Context ContainerRequestContext crc,
17941794
@PathParam("identifier") String dvIdtf,
1795+
@FormDataParam("type") String type,
1796+
@FormDataParam("dvObject") String dvObjectIdtf,
17951797
@FormDataParam("content") String content,
17961798
@FormDataParam("displayOrder") int displayOrder,
17971799
@FormDataParam("file") InputStream imageFileInputStream,
17981800
@FormDataParam("file") FormDataContentDisposition contentDispositionHeader) {
17991801
Dataverse dataverse;
1802+
DvObject dvObject = null;
18001803
try {
18011804
dataverse = findDataverseOrDie(dvIdtf);
1805+
if (dvObjectIdtf != null) {
1806+
dvObject = findDvoByIdAndFeaturedItemTypeOrDie(dvObjectIdtf, type);
1807+
}
18021808
} catch (WrappedResponse wr) {
18031809
return wr.getResponse();
18041810
}
1805-
NewDataverseFeaturedItemDTO newDataverseFeaturedItemDTO = NewDataverseFeaturedItemDTO.fromFormData(content, displayOrder, imageFileInputStream, contentDispositionHeader);
18061811
try {
1812+
NewDataverseFeaturedItemDTO newDataverseFeaturedItemDTO = NewDataverseFeaturedItemDTO.fromFormData(content, displayOrder, imageFileInputStream, contentDispositionHeader, type, dvObject);
18071813
DataverseFeaturedItem dataverseFeaturedItem = execCommand(new CreateDataverseFeaturedItemCommand(
18081814
createDataverseRequest(getRequestUser(crc)),
18091815
dataverse,
@@ -1837,6 +1843,8 @@ public Response updateFeaturedItems(
18371843
@PathParam("dataverseId") String dvIdtf,
18381844
@FormDataParam("id") List<Long> ids,
18391845
@FormDataParam("content") List<String> contents,
1846+
@FormDataParam("type") List<String> types,
1847+
@FormDataParam("dvObject") List<String> dvObjectIdtf,
18401848
@FormDataParam("displayOrder") List<Integer> displayOrders,
18411849
@FormDataParam("keepFile") List<Boolean> keepFiles,
18421850
@FormDataParam("fileName") List<String> fileNames,
@@ -1848,7 +1856,15 @@ public Response updateFeaturedItems(
18481856
}
18491857

18501858
int size = ids.size();
1851-
if (contents.size() != size || displayOrders.size() != size || keepFiles.size() != size || fileNames.size() != size) {
1859+
if (types == null || types.isEmpty()) {
1860+
types = new ArrayList<>(Collections.nCopies(size, null));
1861+
}
1862+
if (dvObjectIdtf == null || dvObjectIdtf.isEmpty()) {
1863+
dvObjectIdtf = new ArrayList<>(Collections.nCopies(size, null));
1864+
}
1865+
1866+
if (contents.size() != size || displayOrders.size() != size || keepFiles.size() != size || fileNames.size() != size ||
1867+
types.size() != size || dvObjectIdtf.size() != size) {
18521868
throw new WrappedResponse(error(Response.Status.BAD_REQUEST,
18531869
BundleUtil.getStringFromBundle("dataverse.update.featuredItems.error.inputListsSizeMismatch")));
18541870
}
@@ -1873,17 +1889,20 @@ public Response updateFeaturedItems(
18731889
}
18741890
}
18751891

1892+
// ignore dvObject if the id is missing or an empty string
1893+
DvObject dvObject = dvObjectIdtf.get(i) != null && !dvObjectIdtf.get(i).isEmpty()
1894+
? findDvoByIdAndFeaturedItemTypeOrDie(dvObjectIdtf.get(i), types.get(i)) : null;
18761895
if (ids.get(i) == 0) {
18771896
newItems.add(NewDataverseFeaturedItemDTO.fromFormData(
1878-
contents.get(i), displayOrders.get(i), fileInputStream, contentDisposition));
1897+
contents.get(i), displayOrders.get(i), fileInputStream, contentDisposition, types.get(i), dvObject));
18791898
} else {
18801899
DataverseFeaturedItem existingItem = dataverseFeaturedItemServiceBean.findById(ids.get(i));
18811900
if (existingItem == null) {
18821901
throw new WrappedResponse(error(Response.Status.NOT_FOUND,
18831902
MessageFormat.format(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notFound"), ids.get(i))));
18841903
}
18851904
itemsToUpdate.put(existingItem, UpdatedDataverseFeaturedItemDTO.fromFormData(
1886-
contents.get(i), displayOrders.get(i), keepFiles.get(i), fileInputStream, contentDisposition));
1905+
contents.get(i), displayOrders.get(i), keepFiles.get(i), fileInputStream, contentDisposition, types.get(i), dvObject));
18871906
}
18881907
}
18891908

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package edu.harvard.iq.dataverse.api.dto;
2+
3+
import edu.harvard.iq.dataverse.DataFile;
4+
import edu.harvard.iq.dataverse.Dataset;
5+
import edu.harvard.iq.dataverse.Dataverse;
6+
import edu.harvard.iq.dataverse.DvObject;
7+
import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
8+
9+
import java.io.InputStream;
10+
11+
public abstract class AbstractDataverseFeaturedItemDTO {
12+
protected String content;
13+
protected int displayOrder;
14+
protected InputStream imageFileInputStream;
15+
protected String imageFileName;
16+
protected String type;
17+
protected DvObject dvObject;
18+
19+
public void setContent(String content) {
20+
this.content = content;
21+
}
22+
23+
public String getContent() {
24+
return content;
25+
}
26+
27+
public void setDisplayOrder(int displayOrder) {
28+
this.displayOrder = displayOrder;
29+
}
30+
31+
public int getDisplayOrder() {
32+
return displayOrder;
33+
}
34+
35+
public void setImageFileInputStream(InputStream imageFileInputStream) {
36+
this.imageFileInputStream = imageFileInputStream;
37+
}
38+
39+
public InputStream getImageFileInputStream() {
40+
return imageFileInputStream;
41+
}
42+
43+
public void setImageFileName(String imageFileName) {
44+
this.imageFileName = imageFileName;
45+
}
46+
47+
public String getImageFileName() {
48+
return imageFileName;
49+
}
50+
51+
public void setType(String type) {
52+
this.type = type;
53+
}
54+
public String getType() {
55+
return type;
56+
}
57+
public void setDvObject(DvObject dvObject) {
58+
this.dvObject = dvObject;
59+
}
60+
public DvObject getDvObject() {
61+
return dvObject;
62+
}
63+
}

0 commit comments

Comments
 (0)