From 04e09b7933c668e5ed2652ecd3ac499f62a99d35 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 4 Jun 2025 13:48:47 -0400 Subject: [PATCH 01/14] update dvObject featured item response --- ...414-add-dvobject-type-to-featured-items.md | 2 - doc/sphinx-guides/source/api/native-api.rst | 10 ++--- .../dataverse/api/DataverseFeaturedItems.java | 2 +- .../harvard/iq/dataverse/api/Dataverses.java | 4 +- .../iq/dataverse/util/json/JsonPrinter.java | 16 +++++-- .../api/DataverseFeaturedItemsIT.java | 43 +++++++++++++++++-- .../iq/dataverse/api/DataversesIT.java | 11 ++--- .../edu/harvard/iq/dataverse/api/UtilIT.java | 10 ++--- 8 files changed, 72 insertions(+), 26 deletions(-) diff --git a/doc/release-notes/11414-add-dvobject-type-to-featured-items.md b/doc/release-notes/11414-add-dvobject-type-to-featured-items.md index 4abd52697d0..162c83264ef 100644 --- a/doc/release-notes/11414-add-dvobject-type-to-featured-items.md +++ b/doc/release-notes/11414-add-dvobject-type-to-featured-items.md @@ -3,5 +3,3 @@ Dataverse Featured Items can now be linked to Dataverses, Datasets, or Datafiles. Pre-existing featured items as well as new items without dvObjects will be defaulted to type=custom. - -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) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 51a6dae2bb4..9067f3b6e2d 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1218,9 +1218,9 @@ The ``file`` parameter must be specified for each image we want to attach to fea 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. -The ``type`` and ``dvObject`` parameters are optional. These allow you to link the featured item to a Dataverse, Dataset, or Datafile. -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. -If no ``dvObject`` is passed the ``type`` will default to "custom" designating no linked object. +The ``type`` and ``dvObjectIdentifier`` parameters are optional. These allow you to link the featured item to a Dataverse, Dataset, or Datafile. +The ``dvObjectIdentifier`` 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. +If no ``dvObjectIdentifier`` is passed the ``type`` will default to "custom" designating no linked object. Note that any existing featured item not included in the call with its associated identifier and corresponding properties will be removed from the collection. @@ -1250,7 +1250,7 @@ The following example creates two featured items, with an image and a dataset as -F "keepFile=false" -F "keepFile=false" \ -F "file=@$SECOND_ITEM_IMAGE_FILENAME" \ -F "type=" -F "type=@$SECOND_ITEM_TYPE" \ - -F "dvObject=" -F "dvObject=@$SECOND_ITEM_DVOBJECT" \ + -F "dvObjectIdentifier=" -F "dvObjectIdentifier=@$SECOND_ITEM_DVOBJECT" \ "$SERVER_URL/api/dataverses/$ID/featuredItems" @@ -1267,7 +1267,7 @@ The fully expanded example above (without environment variables) looks like this -F "keepFile=false" -F "keepFile=false" \ -F "file=@image.png" \ -F "type=" -F "type=dataset" \ - -F "dvObject=" -F "dvObject=doi:ZZ7/MOSEISLEYDB94" \ + -F "dvObjectIdentifier=" -F "dvObjectIdentifier=doi:ZZ7/MOSEISLEYDB94" \ "https://demo.dataverse.org/api/dataverses/root/featuredItems" 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": diff --git a/src/main/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItems.java b/src/main/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItems.java index f45e9641e6b..30c3146fbfb 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItems.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItems.java @@ -53,7 +53,7 @@ public Response updateFeaturedItem(@Context ContainerRequestContext crc, @PathParam("id") Long id, @FormDataParam("content") String content, @FormDataParam("type") String type, - @FormDataParam("dvObject") String dvObjectIdtf, + @FormDataParam("dvObjectIdentifier") String dvObjectIdtf, @FormDataParam("displayOrder") int displayOrder, @FormDataParam("keepFile") boolean keepFile, @FormDataParam("file") InputStream imageFileInputStream, 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 dcab1519d77..b1d56b6b8a9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -1793,7 +1793,7 @@ public Response getUserPermissionsOnDataverse(@Context ContainerRequestContext c public Response createFeaturedItem(@Context ContainerRequestContext crc, @PathParam("identifier") String dvIdtf, @FormDataParam("type") String type, - @FormDataParam("dvObject") String dvObjectIdtf, + @FormDataParam("dvObjectIdentifier") String dvObjectIdtf, @FormDataParam("content") String content, @FormDataParam("displayOrder") int displayOrder, @FormDataParam("file") InputStream imageFileInputStream, @@ -1844,7 +1844,7 @@ public Response updateFeaturedItems( @FormDataParam("id") List ids, @FormDataParam("content") List contents, @FormDataParam("type") List types, - @FormDataParam("dvObject") List dvObjectIdtf, + @FormDataParam("dvObjectIdentifier") List dvObjectIdtf, @FormDataParam("displayOrder") List displayOrders, @FormDataParam("keepFile") List keepFiles, @FormDataParam("fileName") List fileNames, diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java index 9d3d1ceae20..efc9892ee19 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java @@ -1493,13 +1493,23 @@ public static JsonArrayBuilder jsonDataverseFeaturedItems(List keepFiles, List pathsToFiles, List dvTypes, - List dvObjects, + List dvObjectIdentifiers, String apiToken) { RequestSpecification requestSpec = given() @@ -4747,8 +4747,8 @@ static Response updateDataverseFeaturedItems( if (dvTypes != null && !dvTypes.isEmpty()) { requestSpec.multiPart("type", dvTypes.get(i)); } - if (dvObjects != null && !dvObjects.isEmpty()) { - requestSpec.multiPart("dvObject", dvObjects.get(i)); + if (dvObjectIdentifiers != null && !dvObjectIdentifiers.isEmpty()) { + requestSpec.multiPart("dvObjectIdentifier", dvObjectIdentifiers.get(i)); } String pathToFile = pathsToFiles != null ? pathsToFiles.get(i) : null; From 254941c3532839b3d6366244f98fb4c930607a9b Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 4 Jun 2025 13:53:54 -0400 Subject: [PATCH 02/14] fix doc --- doc/sphinx-guides/source/api/native-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 9067f3b6e2d..c3299513c54 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -1219,7 +1219,7 @@ The ``file`` parameter must be specified for each image we want to attach to fea 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. The ``type`` and ``dvObjectIdentifier`` parameters are optional. These allow you to link the featured item to a Dataverse, Dataset, or Datafile. -The ``dvObjectIdentifier`` 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. +The ``dvObjectIdentifier`` can be passed as the alias, id, or the persistent identifier and the ``type`` must be passed as either "dataverse", "dataset", or "datafile", depending on the type of object. If no ``dvObjectIdentifier`` is passed the ``type`` will default to "custom" designating no linked object. Note that any existing featured item not included in the call with its associated identifier and corresponding properties will be removed from the collection. From 784a2149eead0940fc99b77d6901409a7a9d786f Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:30:53 -0400 Subject: [PATCH 03/14] fix flyway script name --- src/main/resources/db/migration/{V6.5.0.13.sql => V6.6.0.1.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V6.5.0.13.sql => V6.6.0.1.sql} (100%) diff --git a/src/main/resources/db/migration/V6.5.0.13.sql b/src/main/resources/db/migration/V6.6.0.1.sql similarity index 100% rename from src/main/resources/db/migration/V6.5.0.13.sql rename to src/main/resources/db/migration/V6.6.0.1.sql From c56b6a8d475ac41b3058a8f774743d61dcde4190 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:34:35 -0400 Subject: [PATCH 04/14] add checks for restricted files and unpublished dataverse and dataset --- .../featured/DataverseFeaturedItem.java | 8 +++ src/main/java/propertyFiles/Bundle.properties | 2 + .../api/DataverseFeaturedItemsIT.java | 56 ++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java index 416af7dc729..086449857c2 100644 --- a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java +++ b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java @@ -11,6 +11,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Size; +import org.apache.commons.text.CaseUtils; import java.util.Arrays; import java.util.List; @@ -144,6 +145,13 @@ public static void validateTypeAndDvObject(String dvIdtf, DvObject dvObject, Dat (dvObject instanceof DataFile && dvType != DataverseFeaturedItem.TYPES.DATAFILE)) { throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverse.update.featuredItems.error.typeAndDvObjectMismatch")); } + if (dvObject instanceof DataFile) { + if (((DataFile)dvObject).isRestricted()) { + throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.restricted")); + } + } else if (!dvObject.isReleased()) { + throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of(CaseUtils.toCamelCase(dvType.name(), true)))); + } } else { if (dvType != DataverseFeaturedItem.TYPES.CUSTOM) { throw new IllegalArgumentException(BundleUtil.getStringFromBundle("find.dvo.error.dvObjectNotFound", List.of(dvIdtf == null ? "unknown" : dvIdtf))); diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties index 3ab97ebb923..07cf7568731 100644 --- a/src/main/java/propertyFiles/Bundle.properties +++ b/src/main/java/propertyFiles/Bundle.properties @@ -3196,6 +3196,8 @@ sendfeedback.fromEmail.error.invalid=Invalid fromEmail: {0} #DataverseFeaturedItems.java dataverseFeaturedItems.errors.notFound=Could not find dataverse featured item with identifier {0} +dataverseFeaturedItems.errors.notPublished={0} must be published to be featured. +dataverseFeaturedItems.errors.restricted=Datafile must not be restricted to be featured. dataverseFeaturedItems.delete.successful=Successfully deleted dataverse featured item with identifier {0} #DatasetFieldValidator.java diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index 89145fbbfb9..639d59de25d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -20,6 +20,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DataverseFeaturedItemsIT { @@ -35,6 +36,8 @@ public void testCreateFeaturedItemWithDvOdbject() { Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetPersistentId); createFeatureItemResponse.prettyPrint(); @@ -63,6 +66,57 @@ public void testCreateFeaturedItemWithDvOdbject() { assertEquals(fileId, dvObjectIdentifier); } + @Test + public void testUnpublishedPublishedDatasetFeatureItem() { + String apiToken = createUserAndGetApiToken(); + String dataverseAlias = createDataverseAndGetAlias(apiToken); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + createDatasetResponse.prettyPrint(); + String datasetId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); + Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of("Dataset")))); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(OK.getStatusCode()); + } + + @Test + public void testRestrictedUnrestrictedDatafileFeatureItem() { + String apiToken = createUserAndGetApiToken(); + String dataverseAlias = createDataverseAndGetAlias(apiToken); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + createDatasetResponse.prettyPrint(); + String datasetId = String.valueOf(UtilIT.getDatasetIdFromResponse(createDatasetResponse)); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + + String pathToFile = "scripts/search/data/tabular/50by1000.dta"; + Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId, pathToFile, apiToken); + uploadFileResponse.prettyPrint(); + JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); + String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); + assertTrue(UtilIT.sleepForLock(datasetId, "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration"); + + UtilIT.restrictFile(fileId, true, apiToken).prettyPrint(); + + Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.restricted"))); + + UtilIT.restrictFile(fileId, false, apiToken).prettyPrint(); + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(OK.getStatusCode()); + } @Test public void testDeleteFeaturedItem() { String apiToken = createUserAndGetApiToken(); @@ -98,7 +152,7 @@ public void testUpdateFeaturedItem() { Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); - Integer dataverseId = UtilIT.getDataverseIdFromResponse(createDataverseResponse); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); Long featuredItemId = createFeaturedItemAndGetId(dataverseAlias, apiToken, "src/test/resources/images/coffeeshop.png"); // Should return not found when passing incorrect item id From c6493b8eea16afdfb1118357b8c1e3dcffb5452b Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:56:35 -0400 Subject: [PATCH 05/14] fix unit test --- .../command/impl/CreateDataverseFeaturedItemCommandTest.java | 3 +++ .../iq/dataverse/featured/DataverseFeaturedItemTest.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/CreateDataverseFeaturedItemCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/CreateDataverseFeaturedItemCommandTest.java index 9d79251d4a5..0171997e69f 100644 --- a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/CreateDataverseFeaturedItemCommandTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/CreateDataverseFeaturedItemCommandTest.java @@ -19,7 +19,9 @@ import java.io.IOException; import java.io.InputStream; +import java.sql.Timestamp; import java.text.MessageFormat; +import java.time.Instant; import java.util.List; import static edu.harvard.iq.dataverse.mocks.MocksFactory.makeRequest; @@ -45,6 +47,7 @@ void setUp() throws AbstractApiBean.WrappedResponse { testDataverse = new Dataverse(); testDataverse.setId(123L); + testDataverse.setPublicationDate(Timestamp.from(Instant.now())); testNewDataverseFeaturedItemDTO = new NewDataverseFeaturedItemDTO(); testNewDataverseFeaturedItemDTO.setImageFileName("test.png"); diff --git a/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java b/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java index 6f58a0a569f..df5fe81fc2b 100644 --- a/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.sql.Timestamp; +import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -28,6 +30,7 @@ public void setUp() { @Test public void test_validTypeAndDvObject() { // set a Dataset + ds.setPublicationDate(Timestamp.from(Instant.now())); dfi.setDvObject("Dataset", ds); assertEquals("dataset", dfi.getType()); assertEquals(ds, dfi.getDvObject()); From dce46baac12d645de39ab2b51d4713f85253bd66 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:20:08 -0400 Subject: [PATCH 06/14] fix it test by piblishing dataverse and dataset --- src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java | 4 ++++ 1 file changed, 4 insertions(+) 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 4208d1c4803..c3c3158213c 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java @@ -1673,10 +1673,12 @@ public void testCreateFeaturedItem() { Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).prettyPrint(); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); createDatasetResponse.prettyPrint(); String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); String pathToFile1 = "src/main/webapp/resources/images/cc0.png"; Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile1, apiToken); uploadFileResponse.prettyPrint(); @@ -1798,9 +1800,11 @@ public void testUpdateFeaturedItems() { createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); String baseUri = UtilIT.getRestAssuredBaseUri(); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).prettyPrint(); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); // Create new items From 76b2c89e10064b92b0cb0de9f57f524d393d069c Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Tue, 10 Jun 2025 12:36:42 -0400 Subject: [PATCH 07/14] Update src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java Co-authored-by: Guillermo Portas --- .../iq/dataverse/util/json/JsonPrinter.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java index efc9892ee19..169fe51dcff 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java @@ -1501,14 +1501,19 @@ public static JsonObjectBuilder json(DataverseFeaturedItem dataverseFeaturedItem .add("displayOrder", dataverseFeaturedItem.getDisplayOrder()) .add("type", dataverseFeaturedItem.getType()); - if (dataverseFeaturedItem.getDvObject() != null) { - if (dataverseFeaturedItem.getDvObject().isInstanceofDataverse()) { - job.add("dvObjectIdentifier", ((Dataverse) dataverseFeaturedItem.getDvObject()).getAlias()); - } else if (dataverseFeaturedItem.getDvObject().getGlobalId() != null) { - job.add("dvObjectIdentifier", dataverseFeaturedItem.getDvObject().getGlobalId().asString()); + DvObject dvObject = dataverseFeaturedItem.getDvObject(); + if (dvObject != null) { + String identifier; + + if (dvObject.isInstanceofDataverse()) { + identifier = ((Dataverse) dvObject).getAlias(); + } else if (dvObject.getGlobalId() != null) { + identifier = dvObject.getGlobalId().asString(); } else { - job.add("dvObjectIdentifier", String.valueOf(dataverseFeaturedItem.getDvObject().getId())); + identifier = String.valueOf(dvObject.getId()); } + + job.add("dvObjectIdentifier", identifier); } return job; } From 056828946c213ef17cd2c20568ac13704077d77d Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Tue, 10 Jun 2025 14:23:38 -0400 Subject: [PATCH 08/14] fix datafile not found error and added tests --- .../iq/dataverse/api/AbstractApiBean.java | 9 ++++- .../api/DataverseFeaturedItemsIT.java | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java index 92455378209..018657bff4d 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java @@ -590,7 +590,14 @@ protected DvObject findDvo(@NotNull final String id) throws WrappedResponse { protected DvObject findDvoByIdAndFeaturedItemTypeOrDie(@NotNull final String dvIdtf, String type) throws WrappedResponse { try { DataverseFeaturedItem.TYPES dvType = DataverseFeaturedItem.getDvType(type); - DvObject dvObject = isNumeric(dvIdtf) ? findDvo(Long.valueOf(dvIdtf)) : null; + DvObject dvObject = null; + if (isNumeric(dvIdtf)) { + try { + dvObject = findDvo(Long.valueOf(dvIdtf)); + } catch (Exception e) { + throw new WrappedResponse(error(Response.Status.BAD_REQUEST,BundleUtil.getStringFromBundle("find.dvo.error.dvObjectNotFound", Arrays.asList(dvIdtf)))); + } + } if (dvObject == null) { List types = new ArrayList<>(); types.addAll(List.of(DataverseFeaturedItem.TYPES.values())); diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index 639d59de25d..d3a4ba8b2b5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -66,6 +66,46 @@ public void testCreateFeaturedItemWithDvOdbject() { assertEquals(fileId, dvObjectIdentifier); } + @Test + public void testCreateFeaturedItemWithBadDvOdbjectIds() { + String apiToken = createUserAndGetApiToken(); + String dataverseAlias = createDataverseAndGetAlias(apiToken); + Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse) + "BAD"; + Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + String datasetPersistentIdBad = datasetPersistentId + "BAD"; + String dataverseAliasBad = dataverseAlias + "BAD"; + String fieldIdBad = "999"; + + // Test with bad Dataset id should return bad request with not found message + Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetPersistentIdBad); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("find.dvo.error.dvObjectNotFound", List.of(datasetPersistentIdBad)))); + + // Test with bad dataverse alias should return bad request with not found message + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataverse", dataverseAliasBad); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("find.dvo.error.dvObjectNotFound", List.of(dataverseAliasBad)))); + + String pathToFile = "scripts/search/data/tabular/50by1000.dta"; + Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); + uploadFileResponse.prettyPrint(); + JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); + + // Test with bad Datafile id should return bad request with not found message + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fieldIdBad); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("find.dvo.error.dvObjectNotFound", List.of(fieldIdBad)))); + } + @Test public void testUnpublishedPublishedDatasetFeatureItem() { String apiToken = createUserAndGetApiToken(); From cf690d3c57cd2487465154c61b0ef9aec8154ad8 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:20:54 -0400 Subject: [PATCH 09/14] add comments to new IT tests --- .../api/DataverseFeaturedItemsIT.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index d3a4ba8b2b5..f19532b152f 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -31,6 +31,7 @@ public static void setUpClass() { @Test public void testCreateFeaturedItemWithDvOdbject() { + // Set up a new published dataverse and dataset String apiToken = createUserAndGetApiToken(); String dataverseAlias = createDataverseAndGetAlias(apiToken); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); @@ -39,6 +40,7 @@ public void testCreateFeaturedItemWithDvOdbject() { UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + // Test creating a featured item of type Dataset with good persistent id. Returns OK Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetPersistentId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat().statusCode(OK.getStatusCode()); @@ -46,6 +48,7 @@ public void testCreateFeaturedItemWithDvOdbject() { String dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(datasetPersistentId, dvObjectIdentifier); + // Test creating a featured item of type Dataverse with good dataverse alias. Returns OK createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataverse", dataverseAlias); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat().statusCode(OK.getStatusCode()); @@ -53,11 +56,14 @@ public void testCreateFeaturedItemWithDvOdbject() { dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(dataverseAlias, dvObjectIdentifier); + // Upload a file String pathToFile = "scripts/search/data/tabular/50by1000.dta"; Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); uploadFileResponse.prettyPrint(); JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); + + // Test creating a featured item of type Datafile with good file id. Returns OK createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat().statusCode(OK.getStatusCode()); @@ -68,6 +74,7 @@ public void testCreateFeaturedItemWithDvOdbject() { @Test public void testCreateFeaturedItemWithBadDvOdbjectIds() { + // Set up a new published dataverse and dataset String apiToken = createUserAndGetApiToken(); String dataverseAlias = createDataverseAndGetAlias(apiToken); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); @@ -108,18 +115,25 @@ public void testCreateFeaturedItemWithBadDvOdbjectIds() { @Test public void testUnpublishedPublishedDatasetFeatureItem() { + // Set up a new dataverse and dataset without publishing String apiToken = createUserAndGetApiToken(); String dataverseAlias = createDataverseAndGetAlias(apiToken); UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); createDatasetResponse.prettyPrint(); String datasetId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); + + // Test creating a featured item of type Dataset with good persistent id. Returns Bad request due to the dataset not being published Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat() .statusCode(BAD_REQUEST.getStatusCode()) .body("message", equalTo(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of("Dataset")))); + + // Publish the Dataset UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + + // Test creating a featured item of type Dataset with good persistent id. Returns OK due to the dataset being published createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat() @@ -128,6 +142,7 @@ public void testUnpublishedPublishedDatasetFeatureItem() { @Test public void testRestrictedUnrestrictedDatafileFeatureItem() { + // Set up a new dataverse and dataset without publishing String apiToken = createUserAndGetApiToken(); String dataverseAlias = createDataverseAndGetAlias(apiToken); UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); @@ -136,6 +151,7 @@ public void testRestrictedUnrestrictedDatafileFeatureItem() { String datasetId = String.valueOf(UtilIT.getDatasetIdFromResponse(createDatasetResponse)); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + // Upload a file String pathToFile = "scripts/search/data/tabular/50by1000.dta"; Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId, pathToFile, apiToken); uploadFileResponse.prettyPrint(); @@ -143,15 +159,20 @@ public void testRestrictedUnrestrictedDatafileFeatureItem() { String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); assertTrue(UtilIT.sleepForLock(datasetId, "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration"); + // Restrict the file UtilIT.restrictFile(fileId, true, apiToken).prettyPrint(); + // Test creating a featured item of type Datafile with good file id. Returns Bad request due to the datafile being restricted Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat() .statusCode(BAD_REQUEST.getStatusCode()) .body("message", equalTo(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.restricted"))); + // Un-restrict the file UtilIT.restrictFile(fileId, false, apiToken).prettyPrint(); + + // Test creating a featured item of type Datafile with good file id. Returns OK request due to the datafile being un-restricted createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); createFeatureItemResponse.prettyPrint(); createFeatureItemResponse.then().assertThat() @@ -180,6 +201,7 @@ public void testDeleteFeaturedItem() { .body("data.message", equalTo(MessageFormat.format(BundleUtil.getStringFromBundle("dataverseFeaturedItems.delete.successful"), featuredItemId))) .assertThat().statusCode(OK.getStatusCode()); + // Show there are no featured items as they have been deleted Response listFeaturedItemsResponse = UtilIT.listDataverseFeaturedItems(dataverseAlias, apiToken); listFeaturedItemsResponse.then() .body("data.size()", equalTo(0)) From 458042c440ce69079e78e4bec0345ba3309cca7b Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 11 Jun 2025 11:33:01 -0400 Subject: [PATCH 10/14] add dvObject display name to json --- .../iq/dataverse/util/json/JsonPrinter.java | 13 ++- .../dataverse/util/json/JsonPrinterTest.java | 79 +++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java index 169fe51dcff..fe1d96ce761 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java @@ -1503,17 +1503,22 @@ public static JsonObjectBuilder json(DataverseFeaturedItem dataverseFeaturedItem DvObject dvObject = dataverseFeaturedItem.getDvObject(); if (dvObject != null) { - String identifier; + String identifier = null; + String displayName = null; if (dvObject.isInstanceofDataverse()) { identifier = ((Dataverse) dvObject).getAlias(); - } else if (dvObject.getGlobalId() != null) { - identifier = dvObject.getGlobalId().asString(); - } else { + displayName = ((Dataverse) dvObject).getName(); + } else if (dvObject.isInstanceofDataset()) { + identifier = dvObject.getGlobalId() != null ? dvObject.getGlobalId().asString() : String.valueOf(dvObject.getId()); + displayName = ((Dataset) dvObject).getCurrentName(); + } else if (dvObject.isInstanceofDataFile()) { identifier = String.valueOf(dvObject.getId()); + displayName = ((DataFile) dvObject).getDisplayName(); } job.add("dvObjectIdentifier", identifier); + job.add("dvObjectDisplayName", displayName); } return job; } diff --git a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java index 9d37a554820..42eb3a184c9 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java @@ -5,11 +5,15 @@ import edu.harvard.iq.dataverse.authorization.DataverseRole; import edu.harvard.iq.dataverse.authorization.RoleAssignee; import edu.harvard.iq.dataverse.authorization.users.PrivateUrlUser; +import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem; import edu.harvard.iq.dataverse.mocks.MockDatasetFieldSvc; +import edu.harvard.iq.dataverse.pidproviders.doi.AbstractDOIProvider; import edu.harvard.iq.dataverse.privateurl.PrivateUrl; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; import edu.harvard.iq.dataverse.UserNotification.Type; +import java.sql.Timestamp; +import java.time.Instant; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; @@ -405,4 +409,79 @@ public void testMetadataBlockAnonymized() { assertEquals("primitive", actualAuthorJsonObject.getString("typeClass")); assertFalse(actualAuthorJsonObject.getBoolean("multiple")); } + + @Test + public void testDataverseFeaturedItemDataverseTest() { + Dataverse dataverse = createDataverse(42); + DvObject dvObject = createDataverse(1); + + DataverseFeaturedItem fi = new DataverseFeaturedItem(); + fi.setDataverse(dataverse); + fi.setContent(null); + fi.setDisplayOrder(0); + fi.setImageFileName("testfile"); + + fi.setDvObject("dataverse", dvObject); + JsonObjectBuilder jsonObject = JsonPrinter.json(fi); + + assertNotNull(jsonObject); + } + @Test + public void testDataverseFeaturedItemDatasetTest() { + Dataverse dataverse = createDataverse(42); + DvObject dvObject = createDataset(1); + + DataverseFeaturedItem fi = new DataverseFeaturedItem(); + fi.setDataverse(dataverse); + fi.setContent(null); + fi.setDisplayOrder(0); + fi.setImageFileName("testfile"); + + fi.setDvObject("dataset", dvObject); + JsonObject jsonObject = JsonPrinter.json(fi).build(); + assertNotNull(jsonObject); + + System.out.println("json: " + JsonUtil.prettyPrint(jsonObject.toString())); + assertEquals("doi:10.5072/FK2/BYM3IW", jsonObject.getString("dvObjectIdentifier")); + assertEquals("Dataset Tile 1", jsonObject.getString("dvObjectDisplayName")); + + assertNotNull(jsonObject); + } + + private Dataverse createDataverse(long id) { + Dataverse dataverse = new Dataverse(); + dataverse.setId(id); + dataverse.setAlias("dv" + id); + dataverse.setName("Dataverse " + id); + dataverse.setAffiliation(id + " Inc."); + dataverse.setDescription("Description for Dataverse " + id + "."); + dataverse.setPublicationDate(Timestamp.from(Instant.now())); + return dataverse; + } + private Dataset createDataset(long id) { + Dataset dataset = new Dataset(); + DatasetVersion dsVersion = new DatasetVersion(); + dsVersion.setDataset(dataset); + dsVersion.setVersion(1L); + List dsFields = new ArrayList<>(); + DatasetField titleField = new DatasetField(); + DatasetFieldType dsft = new DatasetFieldType(); + DatasetFieldValue dsfv = new DatasetFieldValue(); + dsfv.setValue("Dataset Tile " + id); + dsfv.setDatasetField(titleField); + dsft.setName(DatasetFieldConstant.title); + dsft.setFieldType(FieldType.TEXT); + titleField.setDatasetFieldType(dsft); + titleField.setDatasetFieldValues(List.of(dsfv)); + dsFields.add(titleField); + dsVersion.setDatasetFields(dsFields); + dsVersion.setVersionState(DatasetVersion.VersionState.RELEASED); + dataset.setId(id); + + dataset.setVersions(List.of(dsVersion)); + dataset.setPublicationDate(Timestamp.from(Instant.now())); + dataset.setGlobalId(new GlobalId(AbstractDOIProvider.DOI_PROTOCOL,"10.5072","FK2/BYM3IW", "/", AbstractDOIProvider.DOI_RESOLVER_URL, null)); + + return dataset; + } } From c6b1bcdbb0eb9c952a0f0971e0be7dbcf95c118a Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:07:08 -0400 Subject: [PATCH 11/14] add new tests --- .../api/DataverseFeaturedItemsIT.java | 37 +++++++++---- .../dataverse/util/json/JsonPrinterTest.java | 53 ++++++++++++++++--- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index f19532b152f..cb66246a31d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -40,6 +40,12 @@ public void testCreateFeaturedItemWithDvOdbject() { UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + // Get the created dataset so we can get the title + Response getDatasetResponse = UtilIT.getDatasetVersion(datasetPersistentId, "1", apiToken); + getDatasetResponse.prettyPrint(); + JsonPath createdDataset = JsonPath.from(getDatasetResponse.body().asString()); + String datasetTitle = createdDataset.getString("data.metadataBlocks.citation.fields[0].value"); + // Test creating a featured item of type Dataset with good persistent id. Returns OK Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataset", datasetPersistentId); createFeatureItemResponse.prettyPrint(); @@ -47,6 +53,8 @@ public void testCreateFeaturedItemWithDvOdbject() { JsonPath createdFeaturedItem = JsonPath.from(createFeatureItemResponse.body().asString()); String dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(datasetPersistentId, dvObjectIdentifier); + String dvObjectDisplayName = createdFeaturedItem.getString("data.dvObjectDisplayName"); + assertEquals(datasetTitle, dvObjectDisplayName); // Test creating a featured item of type Dataverse with good dataverse alias. Returns OK createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "dataverse", dataverseAlias); @@ -55,9 +63,12 @@ public void testCreateFeaturedItemWithDvOdbject() { createdFeaturedItem = JsonPath.from(createFeatureItemResponse.body().asString()); dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(dataverseAlias, dvObjectIdentifier); + dvObjectDisplayName = createdFeaturedItem.getString("data.dvObjectDisplayName"); + assertEquals(dataverseAlias, dvObjectDisplayName); // create dataverse sets the name = alias // Upload a file - String pathToFile = "scripts/search/data/tabular/50by1000.dta"; + String fileName = "50by1000.dta"; + String pathToFile = "scripts/search/data/tabular/" + fileName; Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); uploadFileResponse.prettyPrint(); JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); @@ -70,6 +81,8 @@ public void testCreateFeaturedItemWithDvOdbject() { createdFeaturedItem = JsonPath.from(createFeatureItemResponse.body().asString()); dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(fileId, dvObjectIdentifier); + dvObjectDisplayName = createdFeaturedItem.getString("data.dvObjectDisplayName"); + assertEquals(fileName, dvObjectDisplayName); } @Test @@ -214,6 +227,7 @@ public void testUpdateFeaturedItem() { Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); + String dataverseName = dataverseAlias; // createRandomDataverse sets name = alias UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); Long featuredItemId = createFeaturedItemAndGetId(dataverseAlias, apiToken, "src/test/resources/images/coffeeshop.png"); @@ -230,26 +244,26 @@ public void testUpdateFeaturedItem() { // Update featured item: keep image file updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 1, true, null, apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 1,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 1,"custom", null, null); // Update featured item: remove image file updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, null, apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", null, 2,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", null, 2,"custom", null, null); // Update featured item: set new image file updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, "src/test/resources/images/coffeeshop.png", apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 2,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 2,"custom", null, null); // Update featured item: set malicious content which should be sanitized String unsafeContent = "

A title

link"; String sanitizedContent = "

A title

link"; updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, unsafeContent, 2, false, "src/test/resources/images/coffeeshop.png", apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, sanitizedContent, "coffeeshop.png", 2,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, sanitizedContent, "coffeeshop.png", 2,"custom", null, null); // Update featured item: set dataverse type updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle2", 3, false, null, "dataverse", dataverseAlias, apiToken); updateFeatureItemResponse.prettyPrint(); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, null, null, 3, "dataverse", dataverseAlias); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, null, null, 3, "dataverse", dataverseAlias, dataverseName); // Test mismatch between type and dvObject updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle2", 3, false, null, "dataset", dataverseAlias, apiToken); @@ -284,7 +298,7 @@ public void testUpdateFeaturedItemUnicode() { * "????????.png" but once we fix the test, "καφενείο.png" should be * asserted. */ - verifyUpdatedFeaturedItem(createFeatureItemResponse, "test", "????????.png", 0,"custom", null); + verifyUpdatedFeaturedItem(createFeatureItemResponse, "test", "????????.png", 0,"custom", null, null); long featuredItemId = JsonPath.from(createFeatureItemResponse.body().asString()).getLong("data.id"); @@ -298,15 +312,15 @@ public void testUpdateFeaturedItemUnicode() { updateFeatureItemResponse.prettyPrint(); // TODO: Fix this REST Assured assertion too (see above). // The equivalent curl command: scripts/issues/11429/update-featured-item.sh - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "????????.png", 1,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "????????.png", 1,"custom", null, null); // remove image updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, null, apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", null, 2,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", null, 2,"custom", null, null); // add non-unicode image updateFeatureItemResponse = UtilIT.updateDataverseFeaturedItem(featuredItemId, "updatedTitle1", 2, false, coffeeShopEnglish, apiToken); - verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 2,"custom", null); + verifyUpdatedFeaturedItem(updateFeatureItemResponse, "updatedTitle1", "coffeeshop.png", 2,"custom", null, null); updateFeatureItemResponse = UtilIT.deleteDataverseFeaturedItem(featuredItemId, apiToken); updateFeatureItemResponse.then().assertThat().statusCode(OK.getStatusCode()); @@ -347,7 +361,7 @@ private Long createFeaturedItemAndGetId(String dataverseAlias, String apiToken, return createdFeaturedItem.getLong("data.id"); } - private void verifyUpdatedFeaturedItem(Response response, String expectedContent, String expectedImageFileName, int expectedDisplayOrder, String type, String dvObject) { + private void verifyUpdatedFeaturedItem(Response response, String expectedContent, String expectedImageFileName, int expectedDisplayOrder, String type, String dvObject, String dvObjectDisplayName) { response.prettyPrint(); response.then().assertThat() .body("data.content", equalTo(expectedContent)) @@ -355,6 +369,7 @@ private void verifyUpdatedFeaturedItem(Response response, String expectedContent .body("data.displayOrder", equalTo(expectedDisplayOrder)) .body("data.type", equalTo(type)) .body("data.dvObjectIdentifier", equalTo(dvObject)) + .body("data.dvObjectDisplayName", equalTo(dvObjectDisplayName)) .statusCode(OK.getStatusCode()); } } diff --git a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java index 42eb3a184c9..0920809b7f8 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java @@ -413,7 +413,7 @@ public void testMetadataBlockAnonymized() { @Test public void testDataverseFeaturedItemDataverseTest() { Dataverse dataverse = createDataverse(42); - DvObject dvObject = createDataverse(1); + Dataverse dvObject = createDataverse(1); DataverseFeaturedItem fi = new DataverseFeaturedItem(); fi.setDataverse(dataverse); @@ -422,14 +422,18 @@ public void testDataverseFeaturedItemDataverseTest() { fi.setImageFileName("testfile"); fi.setDvObject("dataverse", dvObject); - JsonObjectBuilder jsonObject = JsonPrinter.json(fi); - + JsonObject jsonObject = JsonPrinter.json(fi).build(); assertNotNull(jsonObject); + + System.out.println("json: " + JsonUtil.prettyPrint(jsonObject.toString())); + assertEquals(fi.getType(), jsonObject.getString("type")); + assertEquals(dvObject.getAlias(), jsonObject.getString("dvObjectIdentifier")); + assertEquals(dvObject.getName(), jsonObject.getString("dvObjectDisplayName")); } @Test public void testDataverseFeaturedItemDatasetTest() { Dataverse dataverse = createDataverse(42); - DvObject dvObject = createDataset(1); + Dataset dvObject = createDataset(1); DataverseFeaturedItem fi = new DataverseFeaturedItem(); fi.setDataverse(dataverse); @@ -442,8 +446,30 @@ public void testDataverseFeaturedItemDatasetTest() { assertNotNull(jsonObject); System.out.println("json: " + JsonUtil.prettyPrint(jsonObject.toString())); - assertEquals("doi:10.5072/FK2/BYM3IW", jsonObject.getString("dvObjectIdentifier")); - assertEquals("Dataset Tile 1", jsonObject.getString("dvObjectDisplayName")); + assertEquals(fi.getType(), jsonObject.getString("type")); + assertEquals(dvObject.getGlobalId().asString(), jsonObject.getString("dvObjectIdentifier")); + assertEquals(dvObject.getDisplayName(), jsonObject.getString("dvObjectDisplayName")); + } + + @Test + public void testDataverseFeaturedItemDatafileTest() { + Dataverse dataverse = createDataverse(42); + DataFile dvObject = createDatafile(1L); + + DataverseFeaturedItem fi = new DataverseFeaturedItem(); + fi.setDataverse(dataverse); + fi.setContent(null); + fi.setDisplayOrder(0); + fi.setImageFileName("testfile"); + + fi.setDvObject("datafile", dvObject); + JsonObject jsonObject = JsonPrinter.json(fi).build(); + assertNotNull(jsonObject); + + System.out.println("json: " + JsonUtil.prettyPrint(jsonObject.toString())); + assertEquals(fi.getType(), jsonObject.getString("type")); + assertEquals(dvObject.getId().toString(), jsonObject.getString("dvObjectIdentifier")); + assertEquals(dvObject.getDisplayName(), jsonObject.getString("dvObjectDisplayName")); assertNotNull(jsonObject); } @@ -467,7 +493,7 @@ private Dataset createDataset(long id) { DatasetField titleField = new DatasetField(); DatasetFieldType dsft = new DatasetFieldType(); DatasetFieldValue dsfv = new DatasetFieldValue(); - dsfv.setValue("Dataset Tile " + id); + dsfv.setValue("Dataset Title " + id); dsfv.setDatasetField(titleField); dsft.setName(DatasetFieldConstant.title); dsft.setFieldType(FieldType.TEXT); @@ -484,4 +510,17 @@ private Dataset createDataset(long id) { return dataset; } + private DataFile createDatafile(long id) { + DataFile datafile = new DataFile(); + datafile.setId(1L); + datafile.setRestricted(false); + + FileMetadata fm = new FileMetadata(); + fm.setLabel("xyz.txt"); + fm.setDataFile(datafile); + + datafile.setFileMetadatas(List.of(fm)); + + return datafile; + } } From 5d7e8a52e3dd3f3de759bae594f48fd47c8be3e8 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:04:47 -0400 Subject: [PATCH 12/14] rename flyway script from 6.6.0.1 to 6.6.0.2 --- src/main/resources/db/migration/{V6.6.0.1.sql => V6.6.0.2.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/resources/db/migration/{V6.6.0.1.sql => V6.6.0.2.sql} (100%) diff --git a/src/main/resources/db/migration/V6.6.0.1.sql b/src/main/resources/db/migration/V6.6.0.2.sql similarity index 100% rename from src/main/resources/db/migration/V6.6.0.1.sql rename to src/main/resources/db/migration/V6.6.0.2.sql From 5dd7ce8551f88542d5ecbd17560604cb0c505eb7 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:20:29 -0400 Subject: [PATCH 13/14] re-work the deleteion of datafile featured items when publishing dataset --- .../featured/DataverseFeaturedItem.java | 6 +- .../DataverseFeaturedItemServiceBean.java | 28 ++++++-- .../FinalizeDatasetPublicationCommand.java | 3 + .../api/DataverseFeaturedItemsIT.java | 64 ++++++++++++++++--- .../iq/dataverse/api/DataversesIT.java | 29 +++++++-- 5 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java index 086449857c2..563db096c62 100644 --- a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java +++ b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItem.java @@ -146,9 +146,13 @@ public static void validateTypeAndDvObject(String dvIdtf, DvObject dvObject, Dat throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverse.update.featuredItems.error.typeAndDvObjectMismatch")); } if (dvObject instanceof DataFile) { - if (((DataFile)dvObject).isRestricted()) { + DataFile df = (DataFile)dvObject; + if (df.isRestricted()) { throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.restricted")); } + if (!df.isReleased()) { + throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of("Dataset"))); + } } else if (!dvObject.isReleased()) { throw new IllegalArgumentException(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of(CaseUtils.toCamelCase(dvType.name(), true)))); } diff --git a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItemServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItemServiceBean.java index cf8e30bec7e..2598260aa80 100644 --- a/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItemServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/dataverse/featured/DataverseFeaturedItemServiceBean.java @@ -1,10 +1,6 @@ package edu.harvard.iq.dataverse.dataverse.featured; -import com.google.common.collect.Lists; import edu.harvard.iq.dataverse.*; -import edu.harvard.iq.dataverse.authorization.Permission; -import edu.harvard.iq.dataverse.authorization.users.User; -import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.settings.JvmSettings; import edu.harvard.iq.dataverse.util.BundleUtil; import edu.harvard.iq.dataverse.util.FileUtil; @@ -13,7 +9,6 @@ import jakarta.inject.Named; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; @@ -22,12 +17,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.EnumSet; +import java.util.ArrayList; import java.util.List; +import java.util.logging.Logger; @Stateless @Named public class DataverseFeaturedItemServiceBean implements Serializable { + private static final Logger logger = Logger.getLogger(DataverseFeaturedItemServiceBean.class.getCanonicalName()); public static class InvalidImageFileException extends Exception { public InvalidImageFileException(String message) { @@ -70,6 +67,25 @@ public void deleteAllByDvObjectId(Long id) { .executeUpdate(); } + public void deleteInvalidatedFeaturedItemsByDataset(Dataset dataset) { + // Delete any Featured Items that contain Datafiles that were removed or restricted in the latest published version + List featuredItems = findAllByDataverseOrdered(dataset.getOwner()); + for (DataverseFeaturedItem featuredItem : featuredItems) { + if (featuredItem.getDvObject() != null && featuredItem.getType().equalsIgnoreCase(DataverseFeaturedItem.TYPES.DATAFILE.name())) { + DataFile df = (DataFile) featuredItem.getDvObject(); + List latestVersionFileIds = new ArrayList<>(); + dataset.getLatestVersion().getFileMetadatas().stream() + .map(FileMetadata::getId) + .forEachOrdered(latestVersionFileIds::add); + // If the datafile is restricted or part of this dataset but not in the latest version we need to delete the featured item + if (df.isRestricted() || (dataset.getFiles().contains(df) && !latestVersionFileIds.contains(df.getId()))) { + logger.fine("Deleting invalidated Featured Item for " + (df.isRestricted() ? "Restricted" : "Deleted") + "Datafile ID: " + df.getId()); + deleteAllByDvObjectId(df.getId()); + } + } + } + } + public List findAllByDataverseOrdered(Dataverse dataverse) { List items = em .createNamedQuery("DataverseFeaturedItem.findByDataverseOrderedByDisplayOrder", DataverseFeaturedItem.class) diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/FinalizeDatasetPublicationCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/FinalizeDatasetPublicationCommand.java index fa8cfeb810a..9bfdd958189 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/FinalizeDatasetPublicationCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/FinalizeDatasetPublicationCommand.java @@ -246,6 +246,9 @@ public Dataset execute(CommandContext ctxt) throws CommandException { logger.info("Successfully published the dataset "+readyDataset.getGlobalId().asString()); readyDataset = ctxt.em().merge(readyDataset); + + // Delete any Featured Items that are invalidated by publishing this version + ctxt.dataverseFeaturedItems().deleteInvalidatedFeaturedItemsByDataset(readyDataset); return readyDataset; } diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index cb66246a31d..744d16243b5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -37,6 +37,16 @@ public void testCreateFeaturedItemWithDvOdbject() { Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + // Upload a file + String fileName = "50by1000.dta"; + String pathToFile = "scripts/search/data/tabular/" + fileName; + Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); + uploadFileResponse.prettyPrint(); + JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); + String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); + assertTrue(UtilIT.sleepForLock(datasetId, "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration"); + + // Publish Dataverse and Dataset with Datafile UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); @@ -66,14 +76,6 @@ public void testCreateFeaturedItemWithDvOdbject() { dvObjectDisplayName = createdFeaturedItem.getString("data.dvObjectDisplayName"); assertEquals(dataverseAlias, dvObjectDisplayName); // create dataverse sets the name = alias - // Upload a file - String fileName = "50by1000.dta"; - String pathToFile = "scripts/search/data/tabular/" + fileName; - Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); - uploadFileResponse.prettyPrint(); - JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); - String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); - // Test creating a featured item of type Datafile with good file id. Returns OK createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); createFeatureItemResponse.prettyPrint(); @@ -82,7 +84,8 @@ public void testCreateFeaturedItemWithDvOdbject() { dvObjectIdentifier = createdFeaturedItem.getString("data.dvObjectIdentifier"); assertEquals(fileId, dvObjectIdentifier); dvObjectDisplayName = createdFeaturedItem.getString("data.dvObjectDisplayName"); - assertEquals(fileName, dvObjectDisplayName); + String tabFileNameConvert = fileName.substring(0, fileName.indexOf(".dta")) + ".tab"; + assertEquals(tabFileNameConvert, dvObjectDisplayName); } @Test @@ -153,6 +156,43 @@ public void testUnpublishedPublishedDatasetFeatureItem() { .statusCode(OK.getStatusCode()); } + @Test + public void testUnpublishedPublishedDataFileFeatureItem() { + // Set up a new dataverse and dataset without publishing + String apiToken = createUserAndGetApiToken(); + String dataverseAlias = createDataverseAndGetAlias(apiToken); + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken); + Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + createDatasetResponse.prettyPrint(); + String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); + Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); + + // Upload a file to be a Featured Item + String fileName = "50by1000.dta"; + String pathToFile = "scripts/search/data/tabular/" + fileName; + Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile, apiToken); + uploadFileResponse.prettyPrint(); + JsonPath uploadedFile = JsonPath.from(uploadFileResponse.body().asString()); + String fileId = String.valueOf(uploadedFile.getInt("data.files[0].dataFile.id")); + assertTrue(UtilIT.sleepForLock(datasetId, "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration"); + + // Test creating a featured item of type Datafile. Returns Bad request due to the dataset not being published + Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(BAD_REQUEST.getStatusCode()) + .body("message", equalTo(BundleUtil.getStringFromBundle("dataverseFeaturedItems.errors.notPublished", List.of("Dataset")))); + + // Publish the Dataset + UtilIT.publishDatasetViaNativeApi(datasetPersistentId, "major", apiToken).prettyPrint(); + + // Test creating a featured item of type DataFile. Returns OK due to the dataset being published + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); + createFeatureItemResponse.prettyPrint(); + createFeatureItemResponse.then().assertThat() + .statusCode(OK.getStatusCode()); + } + @Test public void testRestrictedUnrestrictedDatafileFeatureItem() { // Set up a new dataverse and dataset without publishing @@ -162,7 +202,6 @@ public void testRestrictedUnrestrictedDatafileFeatureItem() { Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); createDatasetResponse.prettyPrint(); String datasetId = String.valueOf(UtilIT.getDatasetIdFromResponse(createDatasetResponse)); - UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); // Upload a file String pathToFile = "scripts/search/data/tabular/50by1000.dta"; @@ -175,6 +214,9 @@ public void testRestrictedUnrestrictedDatafileFeatureItem() { // Restrict the file UtilIT.restrictFile(fileId, true, apiToken).prettyPrint(); + // Publish the Dataset with the uploaded file + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); + // Test creating a featured item of type Datafile with good file id. Returns Bad request due to the datafile being restricted Response createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); createFeatureItemResponse.prettyPrint(); @@ -184,6 +226,8 @@ public void testRestrictedUnrestrictedDatafileFeatureItem() { // Un-restrict the file UtilIT.restrictFile(fileId, false, apiToken).prettyPrint(); + // Publish the Dataset with the unrestricted file + UtilIT.publishDatasetViaNativeApi(datasetId, "minor", apiToken).prettyPrint(); // Test creating a featured item of type Datafile with good file id. Returns OK request due to the datafile being un-restricted createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, null, "datafile", fileId); 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 c3c3158213c..2e7fc34bd0c 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java @@ -1673,16 +1673,19 @@ public void testCreateFeaturedItem() { Response createDataverseResponse = UtilIT.createRandomDataverse(apiToken); createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); - UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).prettyPrint(); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); createDatasetResponse.prettyPrint(); String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); - UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); String pathToFile1 = "src/main/webapp/resources/images/cc0.png"; Response uploadFileResponse = UtilIT.uploadFileViaNative(datasetId.toString(), pathToFile1, apiToken); uploadFileResponse.prettyPrint(); String datafileId = String.valueOf(UtilIT.getDataFileIdFromResponse(uploadFileResponse)); + assertTrue(UtilIT.sleepForLock(datasetId, "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration"); + + // Publish Dataverse and Dataset with Datafile + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).prettyPrint(); + UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).prettyPrint(); // Should not return any error when not passing a file @@ -1727,9 +1730,9 @@ public void testCreateFeaturedItem() { .statusCode(NOT_FOUND.getStatusCode()); // Testing new dvobject-type featured items - createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, "test dataset", 10, null, "dataset", datasetPersistentId); + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 10, null, "dataset", datasetPersistentId); createFeatureItemResponse.prettyPrint(); - createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, "test datafile", 11, null, "datafile", datafileId); + createFeatureItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 11, null, "datafile", datafileId); createFeatureItemResponse.prettyPrint(); Response listDataverseFeaturedItemsResponse = UtilIT.listDataverseFeaturedItems(dataverseAlias, apiToken); listDataverseFeaturedItemsResponse.prettyPrint(); @@ -1957,6 +1960,7 @@ public void testDeleteFeaturedItemWithDvObject() { createDataverseResponse.then().assertThat().statusCode(CREATED.getStatusCode()); String dataverseAlias = UtilIT.getAliasFromResponse(createDataverseResponse); Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken); + String datasetPersistentId = UtilIT.getDatasetPersistentIdFromResponse(createDatasetResponse); Integer datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse); // Upload a file @@ -1965,6 +1969,11 @@ public void testDeleteFeaturedItemWithDvObject() { uploadFileResponse.prettyPrint(); Integer datafileId = UtilIT.getDataFileIdFromResponse(uploadFileResponse); assertTrue(UtilIT.sleepForLock(datasetId.longValue(), "Ingest", apiToken, UtilIT.MAXIMUM_INGEST_LOCK_DURATION), "Failed test if Ingest Lock exceeds max duration " + pathToFile1); + + // Publish the Dataverse and Dataset + UtilIT.publishDataverseViaNativeApi(dataverseAlias, apiToken).prettyPrint(); + UtilIT.publishDatasetViaNativeApi(datasetPersistentId, "major", apiToken).prettyPrint(); + Response createDataverseFeaturedItemResponse = UtilIT.createDataverseFeaturedItem(dataverseAlias, apiToken, null, 0, pathToFile1, "datafile", String.valueOf(datafileId)); createDataverseFeaturedItemResponse.prettyPrint(); int featuredItemId = UtilIT.getDatasetIdFromResponse(createDataverseFeaturedItemResponse); @@ -1975,15 +1984,23 @@ public void testDeleteFeaturedItemWithDvObject() { .body("data.size()", equalTo(1)) .assertThat().statusCode(OK.getStatusCode()); - // delete file (cascade deletes the featured item) + // delete the file creates a new DRAFT version of the Dataset but the File still exists in the latest published version UtilIT.deleteFile(datafileId,apiToken).prettyPrint(); listFeaturedItemsResponse = UtilIT.listDataverseFeaturedItems(dataverseAlias, apiToken); listFeaturedItemsResponse.prettyPrint(); + listFeaturedItemsResponse.then() + .body("data.size()", equalTo(1)) + .assertThat().statusCode(OK.getStatusCode()); + + // publish the draft version with the file deleted will cause the featured item to be deleted + UtilIT.publishDatasetViaNativeApi(datasetPersistentId, "major", apiToken).prettyPrint(); + listFeaturedItemsResponse = UtilIT.listDataverseFeaturedItems(dataverseAlias, apiToken); + listFeaturedItemsResponse.prettyPrint(); listFeaturedItemsResponse.then() .body("data.size()", equalTo(0)) .assertThat().statusCode(OK.getStatusCode()); - // try to delete the featured item and if it's already deleted (by deleting the file) it should be NOT FOUND + // try to delete the featured item if it's already deleted should be NOT FOUND Response deleteItemResponse = UtilIT.deleteDataverseFeaturedItem(featuredItemId, apiToken); deleteItemResponse.prettyPrint(); deleteItemResponse.then() From 9546ea3e51dcf31a2877b1627f54ce7319160c18 Mon Sep 17 00:00:00 2001 From: Steven Winship <39765413+stevenwinship@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:29:01 -0400 Subject: [PATCH 14/14] fix tests by setting publication date on datafile --- .../harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java | 1 + .../java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java b/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java index df5fe81fc2b..f20a80eadbf 100644 --- a/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/featured/DataverseFeaturedItemTest.java @@ -36,6 +36,7 @@ public void test_validTypeAndDvObject() { assertEquals(ds, dfi.getDvObject()); // update with Datafile + df.setPublicationDate(Timestamp.from(Instant.now())); dfi.setDvObject("Datafile", df); assertEquals("datafile", dfi.getType()); assertEquals(df, dfi.getDvObject()); diff --git a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java index 0920809b7f8..df7045327fd 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/json/JsonPrinterTest.java @@ -455,6 +455,7 @@ public void testDataverseFeaturedItemDatasetTest() { public void testDataverseFeaturedItemDatafileTest() { Dataverse dataverse = createDataverse(42); DataFile dvObject = createDatafile(1L); + dvObject.setPublicationDate(Timestamp.from(Instant.now())); DataverseFeaturedItem fi = new DataverseFeaturedItem(); fi.setDataverse(dataverse);