From 5ee23602760d3d68c53cb554994080953b2d5bd8 Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:31:27 -0400
Subject: [PATCH 01/30] DRAFT using mydata/retrieve API
---
.../iq/dataverse/mydata/DataRetrieverAPI.java | 11 +++++----
.../iq/dataverse/api/DataRetrieverApiIT.java | 23 +++++++++++++++++++
.../edu/harvard/iq/dataverse/api/UtilIT.java | 18 +++++++++++----
3 files changed, 44 insertions(+), 8 deletions(-)
diff --git a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
index 336e7735659..a1c7de54c4b 100644
--- a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
+++ b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
@@ -274,8 +274,10 @@ public String retrieveMyDataAsJsonString(
@QueryParam("role_ids") List roleIds,
@QueryParam("userIdentifier") String userIdentifier,
@QueryParam("filter_validities") Boolean filterValidities,
- @QueryParam("dataset_valid") List datasetValidities) {
+ @QueryParam("dataset_valid") List datasetValidities,
+ @QueryParam("my_data") Boolean my_data) {
boolean OTHER_USER = false;
+ boolean dataRelatedToMe = my_data != null ? my_data : true;
String noMsgResultsFound = BundleUtil.getStringFromBundle("dataretrieverAPI.noMsgResultsFound");
@@ -385,10 +387,11 @@ public String retrieveMyDataAsJsonString(
//SearchFields.NAME_SORT, SortBy.ASCENDING,
SearchFields.RELEASE_OR_CREATE_DATE, SortBy.DESCENDING,
solrCardStart, //paginationStart,
- true, // dataRelatedToMe
+ dataRelatedToMe, // dataRelatedToMe
SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE //10 // SearchFields.NUM_SOLR_DOCS_TO_RETRIEVE
);
-
+ logger.severe(">>>> dataRelatedToMe " + dataRelatedToMe);
+ logger.severe(">>>> this.solrQueryResponse.getNumResultsFound() " + this.solrQueryResponse.getNumResultsFound());
//msgt("getResultsStart: " + this.solrQueryResponse.getResultsStart());
//msgt("getNumResultsFound: " + this.solrQueryResponse.getNumResultsFound());
//msgt("getSolrSearchResults: " + this.solrQueryResponse.getSolrSearchResults().toString());
@@ -527,4 +530,4 @@ private JsonArrayBuilder formatSolrDocs(SolrQueryResponse solrResponse, RoleTagR
private boolean isValid(SolrSearchResult result) {
return result.isValid(x -> true);
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
index c3bfdb80e6e..f7fb73e3161 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
@@ -1,5 +1,6 @@
package edu.harvard.iq.dataverse.api;
+import edu.harvard.iq.dataverse.DvObject;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import edu.harvard.iq.dataverse.api.auth.ApiKeyAuthMechanism;
@@ -90,14 +91,36 @@ public void testRetrieveMyDataAsJsonString() {
assertEquals(1, jsonPathOneDataverse.getInt("data.total_count"));
assertEquals(dataverseAlias, jsonPathOneDataverse.getString("data.items[0].name"));
+ //url = "/api/mydata/retrieve?role_ids=1&role_ids=3&role_ids=5&role_ids=7&dvobject_types=Dataverse&start=#{start}&per_page=#{per_page}&published_states=Published&published_states=Unpublished"
+ createDataverseResponse = UtilIT.createRandomDataverse(superUserApiToken);
+ createDataverseResponse.prettyPrint();
+ String dataverseAlias2 = UtilIT.getAliasFromResponse(createDataverseResponse);
+ //UtilIT.publishDataverseViaNativeApi(dataverseAlias2, superUserApiToken);
+ UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.ADMIN.toString(),
+ "@" + normalUserUsername, superUserApiToken);
+ createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias2, normalUserApiToken);
+ createDatasetResponse.prettyPrint();
+ assertEquals(201, createDatasetResponse.getStatusCode());
+ Integer datasetId2 = UtilIT.getDatasetIdFromResponse(createDatasetResponse);
+ UtilIT.sleepForReindex(datasetId2.toString(), normalUserApiToken, 4);
+
+ Response response = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", Arrays.asList(3L , 5L, 7L), Arrays.asList(DvObject.DType.Dataverse), false);
+ response.prettyPrint();
+
// Clean up
Response deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, normalUserApiToken);
deleteDatasetResponse.prettyPrint();
assertEquals(200, deleteDatasetResponse.getStatusCode());
+ deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId2, normalUserApiToken);
+ deleteDatasetResponse.prettyPrint();
+ assertEquals(200, deleteDatasetResponse.getStatusCode());
Response deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias, normalUserApiToken);
deleteDataverseResponse.prettyPrint();
assertEquals(200, deleteDataverseResponse.getStatusCode());
+ deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias2, superUserApiToken);
+ deleteDataverseResponse.prettyPrint();
+ assertEquals(200, deleteDataverseResponse.getStatusCode());
Response deleteUserResponse = UtilIT.deleteUser(normalUserUsername);
deleteUserResponse.prettyPrint();
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
index e594751297d..32ce3e5ce40 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
@@ -1,6 +1,7 @@
package edu.harvard.iq.dataverse.api;
-import edu.harvard.iq.dataverse.Dataverse;
+import edu.harvard.iq.dataverse.*;
+import edu.harvard.iq.dataverse.search.IndexServiceBean;
import edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
@@ -47,9 +48,7 @@
import static edu.harvard.iq.dataverse.api.ApiConstants.*;
import static io.restassured.path.xml.XmlPath.from;
import static io.restassured.RestAssured.given;
-import edu.harvard.iq.dataverse.DatasetField;
-import edu.harvard.iq.dataverse.DatasetFieldType;
-import edu.harvard.iq.dataverse.DatasetFieldValue;
+
import edu.harvard.iq.dataverse.settings.FeatureFlags;
import edu.harvard.iq.dataverse.util.StringUtil;
@@ -4121,6 +4120,17 @@ static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifie
.get("/api/mydata/retrieve?userIdentifier=" + userIdentifier);
return response;
}
+ static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifier, List roleIds, List objectTypes, boolean myDataOnly) {
+ Response response = given()
+ .header(API_TOKEN_HTTP_HEADER, apiToken)
+ .contentType("application/json; charset=utf-8")
+ .queryParam("role_ids", roleIds)
+ .queryParam("dvobject_types", objectTypes)
+ .queryParam("published_states", MyDataFilterParams.allPublishedStates)
+ .queryParam("my_data", myDataOnly)
+ .get("/api/mydata/retrieve?userIdentifier=" + userIdentifier);
+ return response;
+ }
static Response createSignedUrl(String apiToken, String apiPath, String username) {
Response response = given()
From 2b8d4eed5ac700f9b7ab10b9ce01c4f332fa0dc5 Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:37:54 -0400
Subject: [PATCH 02/30] DRAFT using mydata/retrieve API
---
.../java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
index f7fb73e3161..2bd0e350e2d 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
@@ -96,7 +96,7 @@ public void testRetrieveMyDataAsJsonString() {
createDataverseResponse.prettyPrint();
String dataverseAlias2 = UtilIT.getAliasFromResponse(createDataverseResponse);
//UtilIT.publishDataverseViaNativeApi(dataverseAlias2, superUserApiToken);
- UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.ADMIN.toString(),
+ UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.FULL_CONTRIBUTOR.toString(),
"@" + normalUserUsername, superUserApiToken);
createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias2, normalUserApiToken);
createDatasetResponse.prettyPrint();
@@ -106,6 +106,7 @@ public void testRetrieveMyDataAsJsonString() {
Response response = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", Arrays.asList(3L , 5L, 7L), Arrays.asList(DvObject.DType.Dataverse), false);
response.prettyPrint();
+ assertEquals(2, jsonPathOneDataverse.getInt("data.total_count"));
// Clean up
Response deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, normalUserApiToken);
From f4d017ce24ce61c6c2c75893ebeb20de3fc33762 Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Wed, 23 Jul 2025 16:52:49 -0400
Subject: [PATCH 03/30] DRAFT using mydata/retrieve API
---
.../java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java | 2 --
src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java | 1 -
2 files changed, 3 deletions(-)
diff --git a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
index a1c7de54c4b..b153c1e4f6f 100644
--- a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
+++ b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
@@ -390,8 +390,6 @@ public String retrieveMyDataAsJsonString(
dataRelatedToMe, // dataRelatedToMe
SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE //10 // SearchFields.NUM_SOLR_DOCS_TO_RETRIEVE
);
- logger.severe(">>>> dataRelatedToMe " + dataRelatedToMe);
- logger.severe(">>>> this.solrQueryResponse.getNumResultsFound() " + this.solrQueryResponse.getNumResultsFound());
//msgt("getResultsStart: " + this.solrQueryResponse.getResultsStart());
//msgt("getNumResultsFound: " + this.solrQueryResponse.getNumResultsFound());
//msgt("getSolrSearchResults: " + this.solrQueryResponse.getSolrSearchResults().toString());
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
index 32ce3e5ce40..14cc758e50a 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
@@ -1,7 +1,6 @@
package edu.harvard.iq.dataverse.api;
import edu.harvard.iq.dataverse.*;
-import edu.harvard.iq.dataverse.search.IndexServiceBean;
import edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
From 2dee3f73820ba58e40013f80aae7192c4ccedd15 Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Thu, 24 Jul 2025 15:41:24 -0400
Subject: [PATCH 04/30] adding new api endpoint
---
.../iq/dataverse/mydata/DataRetrieverAPI.java | 132 +++++++++++++++---
.../iq/dataverse/api/DataRetrieverApiIT.java | 80 +++++++++--
.../edu/harvard/iq/dataverse/api/UtilIT.java | 9 +-
3 files changed, 185 insertions(+), 36 deletions(-)
diff --git a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
index b153c1e4f6f..8e54f7dd047 100644
--- a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
+++ b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
@@ -3,13 +3,7 @@
*/
package edu.harvard.iq.dataverse.mydata;
-import edu.harvard.iq.dataverse.DatasetServiceBean;
-import edu.harvard.iq.dataverse.DataverseRoleServiceBean;
-import edu.harvard.iq.dataverse.DataverseServiceBean;
-import edu.harvard.iq.dataverse.DataverseSession;
-import edu.harvard.iq.dataverse.DvObject;
-import edu.harvard.iq.dataverse.DvObjectServiceBean;
-import edu.harvard.iq.dataverse.RoleAssigneeServiceBean;
+import edu.harvard.iq.dataverse.*;
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
import edu.harvard.iq.dataverse.search.SolrQueryResponse;
import edu.harvard.iq.dataverse.search.SolrSearchResult;
@@ -17,7 +11,6 @@
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.DataverseRole;
import edu.harvard.iq.dataverse.authorization.DataverseRolePermissionHelper;
-//import edu.harvard.iq.dataverse.authorization.MyDataQueryHelperServiceBean;
import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
@@ -27,15 +20,13 @@
import edu.harvard.iq.dataverse.search.SearchServiceFactory;
import edu.harvard.iq.dataverse.search.SortBy;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.logging.Logger;
+
+import edu.harvard.iq.dataverse.util.json.JsonPrinter;
import jakarta.ejb.EJB;
import jakarta.inject.Inject;
-import jakarta.json.Json;
-import jakarta.json.JsonArrayBuilder;
-import jakarta.json.JsonObjectBuilder;
+import jakarta.json.*;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@@ -274,10 +265,8 @@ public String retrieveMyDataAsJsonString(
@QueryParam("role_ids") List roleIds,
@QueryParam("userIdentifier") String userIdentifier,
@QueryParam("filter_validities") Boolean filterValidities,
- @QueryParam("dataset_valid") List datasetValidities,
- @QueryParam("my_data") Boolean my_data) {
+ @QueryParam("dataset_valid") List datasetValidities) {
boolean OTHER_USER = false;
- boolean dataRelatedToMe = my_data != null ? my_data : true;
String noMsgResultsFound = BundleUtil.getStringFromBundle("dataretrieverAPI.noMsgResultsFound");
@@ -387,7 +376,7 @@ public String retrieveMyDataAsJsonString(
//SearchFields.NAME_SORT, SortBy.ASCENDING,
SearchFields.RELEASE_OR_CREATE_DATE, SortBy.DESCENDING,
solrCardStart, //paginationStart,
- dataRelatedToMe, // dataRelatedToMe
+ true, // dataRelatedToMe
SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE //10 // SearchFields.NUM_SOLR_DOCS_TO_RETRIEVE
);
//msgt("getResultsStart: " + this.solrQueryResponse.getResultsStart());
@@ -456,7 +445,112 @@ public String retrieveMyDataAsJsonString(
return jsonData.build().toString();
}
-
+
+ @GET
+ @AuthRequired
+ @Path(retrieveDataPartialAPIPath + "/collectionList")
+ @Produces({"application/json"})
+ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @QueryParam("userIdentifier") String userIdentifier) {
+ String noMsgResultsFound = BundleUtil.getStringFromBundle("dataretrieverAPI.noMsgResultsFound");
+ if ((session.getUser() != null) && (session.getUser().isAuthenticated())) {
+ authUser = (AuthenticatedUser) session.getUser();
+ } else {
+ try {
+ authUser = getRequestAuthenticatedUserOrDie(crc);
+ } catch (WrappedResponse e) {
+ return this.getJSONErrorString(
+ BundleUtil.getStringFromBundle("dataretrieverAPI.authentication.required"),
+ BundleUtil.getStringFromBundle("dataretrieverAPI.authentication.required.opt")
+ );
+ }
+ }
+ // If the user is a superuser, see if a userIdentifier has been specified and use that instead
+ AuthenticatedUser searchUser = null;
+ if ((authUser.isSuperuser()) && (userIdentifier != null) && (!userIdentifier.isEmpty())) {
+ searchUser = getUserFromIdentifier(userIdentifier);
+ if (searchUser != null) {
+ authUser = searchUser;
+ } else {
+ return this.getJSONErrorString(
+ BundleUtil.getStringFromBundle("dataretrieverAPI.user.not.found", Arrays.asList(userIdentifier)),
+ null);
+ }
+ }
+ roleList = dataverseRoleService.findAll();
+ rolePermissionHelper = new DataverseRolePermissionHelper(roleList);
+
+ // ---------------------------------
+ // (1) Initialize filterParams and check for Errors
+ // ---------------------------------
+ DataverseRequest dataverseRequest = createDataverseRequest(authUser);
+
+ MyDataFilterParams filterParams = new MyDataFilterParams(dataverseRequest, Arrays.asList(DvObject.DType.Dataverse), MyDataFilterParams.allPublishedStates, Arrays.asList(1L, 3L, 5L, 7L), null, null);
+ if (filterParams.hasError()){
+ return this.getJSONErrorString(filterParams.getErrorMessage(), filterParams.getErrorMessage());
+ }
+
+ // ---------------------------------
+ // (2) Initialize MyDataFinder and check for Errors
+ // ---------------------------------
+ myDataFinder = new MyDataFinder(rolePermissionHelper,
+ roleAssigneeService,
+ dvObjectServiceBean,
+ groupService);
+ this.myDataFinder.runFindDataSteps(filterParams);
+ if (myDataFinder.hasError()){
+ return this.getJSONErrorString(myDataFinder.getErrorMessage(), myDataFinder.getErrorMessage());
+ }
+
+ // ---------------------------------
+ // (3) Make Solr Query
+ // ---------------------------------
+ int paginationStart = 0;
+ int totalCount;
+ List collections = new ArrayList<>();
+ JsonArrayBuilder jsonDataArray = Json.createArrayBuilder();
+
+ List filterQueries = this.myDataFinder.getSolrFilterQueries();
+ if (filterQueries==null){
+ logger.fine("No ids found for this search");
+ return this.getJSONErrorString(noMsgResultsFound, null);
+ }
+ try {
+ do {
+ solrQueryResponse = searchService.getDefaultSearchService().search(
+ dataverseRequest,
+ null, // subtree, default it to Dataverse for now
+ filterParams.getSearchTerm(), //"*", //
+ filterQueries,//filterQueries,
+ SearchFields.RELEASE_OR_CREATE_DATE, SortBy.DESCENDING,
+ paginationStart, //paginationStart,
+ true, // dataRelatedToMe
+ SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE //10 // SearchFields.NUM_SOLR_DOCS_TO_RETRIEVE
+ );
+ if (this.solrQueryResponse.getNumResultsFound() == 0) {
+ return this.getJSONErrorString(noMsgResultsFound, null);
+ }
+ totalCount = solrQueryResponse.getNumResultsFound().intValue();
+
+ for (SolrSearchResult result : solrQueryResponse.getSolrSearchResults()) {
+ if (result.getEntity() instanceof Dataverse) {
+ collections.add((Dataverse) result.getEntity());
+ }
+ paginationStart++;
+ }
+
+ } while (totalCount > paginationStart);
+
+ } catch (SearchException ex) {
+ solrQueryResponse = null;
+ logger.severe("Solr SearchException: " + ex.getMessage());
+ }
+ for (Dataverse dv : collections) {
+ jsonDataArray.add(JsonPrinter.json(dv));
+ }
+
+ return jsonDataArray.build().toString();
+ }
+
private JsonObjectBuilder getDvObjectTypeCounts(SolrQueryResponse solrResponse) {
if (solrQueryResponse == null) {
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
index 2bd0e350e2d..9e818d97b65 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
@@ -30,7 +30,7 @@ public static void setUpClass() {
}
@Test
- public void testRetrieveMyDataAsJsonString() {
+ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
// Call with bad API token
ArrayList emptyRoleIdsList = new ArrayList<>();
Response badApiTokenResponse = UtilIT.retrieveMyDataAsJsonString("bad-token", "dummy-user-identifier", emptyRoleIdsList);
@@ -91,30 +91,28 @@ public void testRetrieveMyDataAsJsonString() {
assertEquals(1, jsonPathOneDataverse.getInt("data.total_count"));
assertEquals(dataverseAlias, jsonPathOneDataverse.getString("data.items[0].name"));
- //url = "/api/mydata/retrieve?role_ids=1&role_ids=3&role_ids=5&role_ids=7&dvobject_types=Dataverse&start=#{start}&per_page=#{per_page}&published_states=Published&published_states=Unpublished"
+ // Use retrieve mydata endpoint to get a list of collections that this user can add Datasets to
createDataverseResponse = UtilIT.createRandomDataverse(superUserApiToken);
createDataverseResponse.prettyPrint();
String dataverseAlias2 = UtilIT.getAliasFromResponse(createDataverseResponse);
- //UtilIT.publishDataverseViaNativeApi(dataverseAlias2, superUserApiToken);
- UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.FULL_CONTRIBUTOR.toString(),
+ // Add Curator role so this collection will show up in the list
+ UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.CURATOR.toString(),
"@" + normalUserUsername, superUserApiToken);
- createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias2, normalUserApiToken);
- createDatasetResponse.prettyPrint();
- assertEquals(201, createDatasetResponse.getStatusCode());
- Integer datasetId2 = UtilIT.getDatasetIdFromResponse(createDatasetResponse);
- UtilIT.sleepForReindex(datasetId2.toString(), normalUserApiToken, 4);
+ // Sleep for indexing
+ Thread.sleep(4000);
+
+ Response retrieveMyDataAsJsonResponse = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", Arrays.asList(1L, 3L , 5L, 7L), Arrays.asList(DvObject.DType.Dataverse));
+ retrieveMyDataAsJsonResponse.prettyPrint();
+ JsonPath jsonPath = retrieveMyDataAsJsonResponse.getBody().jsonPath();
+ assertEquals(2, jsonPath.getInt("data.total_count"));
- Response response = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", Arrays.asList(3L , 5L, 7L), Arrays.asList(DvObject.DType.Dataverse), false);
- response.prettyPrint();
- assertEquals(2, jsonPathOneDataverse.getInt("data.total_count"));
+ retrieveMyDataAsJsonResponse = UtilIT.retrieveMyCollectionList(normalUserApiToken, "");
+ retrieveMyDataAsJsonResponse.prettyPrint();
// Clean up
Response deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, normalUserApiToken);
deleteDatasetResponse.prettyPrint();
assertEquals(200, deleteDatasetResponse.getStatusCode());
- deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId2, normalUserApiToken);
- deleteDatasetResponse.prettyPrint();
- assertEquals(200, deleteDatasetResponse.getStatusCode());
Response deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias, normalUserApiToken);
deleteDataverseResponse.prettyPrint();
@@ -128,6 +126,58 @@ public void testRetrieveMyDataAsJsonString() {
assertEquals(200, deleteUserResponse.getStatusCode());
}
+ // Test getting a list of collections that the user can add datasets to
+ @Test
+ public void testRetrieveMyDataCollections() throws InterruptedException {
+ // Create User1
+ Response createUserResponse = UtilIT.createRandomUser();
+ Response makeSuperUserResponse = UtilIT.makeSuperUser(UtilIT.getUsernameFromResponse(createUserResponse));
+ assertEquals(OK.getStatusCode(), makeSuperUserResponse.getStatusCode());
+ String superUserUsername = UtilIT.getUsernameFromResponse(createUserResponse);
+ String superUserApiToken = UtilIT.getApiTokenFromResponse(createUserResponse);
+
+ // Create User2
+ Response createNormalUserResponse = UtilIT.createRandomUser();
+ String normalUserUsername = UtilIT.getUsernameFromResponse(createNormalUserResponse);
+ String normalUserApiToken = UtilIT.getApiTokenFromResponse(createNormalUserResponse);
+
+ // User1 creates a number of Dataverses and adds a role allowing User2 access
+ List dataverses = new ArrayList<>();
+ for (int i = 0; i <15; i++) {
+ Response createDataverseResponse = UtilIT.createRandomDataverse(superUserApiToken);
+ String alias = UtilIT.getAliasFromResponse(createDataverseResponse);
+ //createDataverseResponse.prettyPrint();
+ dataverses.add(alias);
+ UtilIT.grantRoleOnDataverse(alias, DataverseRole.CURATOR.toString(),
+ "@" + normalUserUsername, superUserApiToken);
+ }
+ // User2 adds their own Dataverse
+ Response createDataverseResponse = UtilIT.createRandomDataverse(normalUserApiToken);
+ String alias = UtilIT.getAliasFromResponse(createDataverseResponse);
+ dataverses.add(alias);
+ // Sleep for indexing
+ Thread.sleep(4000);
+
+ // User2 gets the list of Dataverses/Collections it has access to
+ Response retrieveMyDataAsJsonResponse = UtilIT.retrieveMyCollectionList(normalUserApiToken, "");
+ retrieveMyDataAsJsonResponse.prettyPrint();
+ // The count should show the list size to be User1's + User2's Dataverse count
+ JsonPath jsonPath = retrieveMyDataAsJsonResponse.getBody().jsonPath();
+ assertEquals(dataverses.size(), jsonPath.getList("").size());
+
+ // Clean up
+ dataverses.forEach(dv -> {
+ Response deleteDataverseResponse = UtilIT.deleteDataverse(dv, superUserApiToken);
+ assertEquals(200, deleteDataverseResponse.getStatusCode());
+ });
+ Response deleteUserResponse = UtilIT.deleteUser(normalUserUsername);
+ deleteUserResponse.prettyPrint();
+ assertEquals(200, deleteUserResponse.getStatusCode());
+ deleteUserResponse = UtilIT.deleteUser(superUserUsername);
+ deleteUserResponse.prettyPrint();
+ assertEquals(200, deleteUserResponse.getStatusCode());
+ }
+
@Test
public void testRetrieveMyDataAsJsonStringSortOrder() {
// Create superuser
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
index 14cc758e50a..8ff74625988 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
@@ -4119,17 +4119,22 @@ static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifie
.get("/api/mydata/retrieve?userIdentifier=" + userIdentifier);
return response;
}
- static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifier, List roleIds, List objectTypes, boolean myDataOnly) {
+ static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifier, List roleIds, List objectTypes) {
Response response = given()
.header(API_TOKEN_HTTP_HEADER, apiToken)
.contentType("application/json; charset=utf-8")
.queryParam("role_ids", roleIds)
.queryParam("dvobject_types", objectTypes)
.queryParam("published_states", MyDataFilterParams.allPublishedStates)
- .queryParam("my_data", myDataOnly)
.get("/api/mydata/retrieve?userIdentifier=" + userIdentifier);
return response;
}
+ static Response retrieveMyCollectionList(String apiToken, String userIdentifier) {
+ Response response = given()
+ .header(API_TOKEN_HTTP_HEADER, apiToken)
+ .get("/api/mydata/retrieve/collectionList?userIdentifier=" + userIdentifier);
+ return response;
+ }
static Response createSignedUrl(String apiToken, String apiPath, String username) {
Response response = given()
From 6004421908d6f81b730f8768f3ba200218e343ec Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Thu, 24 Jul 2025 16:12:23 -0400
Subject: [PATCH 05/30] update docs
---
...ve-collections-a-user-can-create-datasets-in.md | 5 +++++
doc/sphinx-guides/source/api/native-api.rst | 14 ++++++++++++++
2 files changed, 19 insertions(+)
create mode 100644 doc/release-notes/11525-retrieve-collections-a-user-can-create-datasets-in.md
diff --git a/doc/release-notes/11525-retrieve-collections-a-user-can-create-datasets-in.md b/doc/release-notes/11525-retrieve-collections-a-user-can-create-datasets-in.md
new file mode 100644
index 00000000000..26898d09412
--- /dev/null
+++ b/doc/release-notes/11525-retrieve-collections-a-user-can-create-datasets-in.md
@@ -0,0 +1,5 @@
+### New API to retrieve a list of collections that an authenticated user can create a dataset in
+
+The API GET /api/mydata/retrieve/collectionList will return all the dataverse objects that the user select from in the SPA UI
+
+See also [the guides](https://guides.dataverse.org/en/latest/api/native-api.html#mydata) and #11525.
diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst
index 7ca91edf51d..0a12440cd64 100644
--- a/doc/sphinx-guides/source/api/native-api.rst
+++ b/doc/sphinx-guides/source/api/native-api.rst
@@ -7752,3 +7752,17 @@ Parameters:
``per_page`` Number of results returned per page.
+MyData Collection List
+----------------------
+
+The MyData Collection List API is used to get a list of the collections an authenticated user can create a Dataset in.
+
+A curl example listing collections
+
+.. code-block:: bash
+
+ export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ export SERVER_URL=https://demo.dataverse.org
+
+ curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/mydata/retrieve/collectionList"
+
From d3d835815997b9b0e35b62b882975bf10031d608 Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Mon, 28 Jul 2025 10:48:19 -0400
Subject: [PATCH 06/30] new endpoint
---
doc/sphinx-guides/source/api/native-api.rst | 4 +-
.../iq/dataverse/mydata/DataRetrieverAPI.java | 14 ++---
.../iq/dataverse/util/json/JsonPrinter.java | 14 ++++-
.../iq/dataverse/api/DataRetrieverApiIT.java | 57 +++++++++++++++----
.../edu/harvard/iq/dataverse/api/UtilIT.java | 20 +++++--
5 files changed, 83 insertions(+), 26 deletions(-)
diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst
index 0a12440cd64..478add8f165 100644
--- a/doc/sphinx-guides/source/api/native-api.rst
+++ b/doc/sphinx-guides/source/api/native-api.rst
@@ -7756,6 +7756,8 @@ MyData Collection List
----------------------
The MyData Collection List API is used to get a list of the collections an authenticated user can create a Dataset in.
+Param identifier={userName} is used by a superuser to get the collections for a specific user.
+Param includeDescriptions=true will include the dataverse description in the list for each dataverse. False of missing will only include the name and alias of the dataverse.
A curl example listing collections
@@ -7764,5 +7766,5 @@ A curl example listing collections
export API_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
export SERVER_URL=https://demo.dataverse.org
- curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/mydata/retrieve/collectionList"
+ curl -H "X-Dataverse-key:$API_TOKEN" "$SERVER_URL/api/mydata/retrieve/collectionList?includeDescriptions=true&identifier="
diff --git a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
index 8e54f7dd047..580c11e9863 100644
--- a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
+++ b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
@@ -450,7 +450,8 @@ public String retrieveMyDataAsJsonString(
@AuthRequired
@Path(retrieveDataPartialAPIPath + "/collectionList")
@Produces({"application/json"})
- public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @QueryParam("userIdentifier") String userIdentifier) {
+ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @QueryParam("userIdentifier") String userIdentifier,
+ @QueryParam("includeDescriptions") Boolean includeDescriptions) {
String noMsgResultsFound = BundleUtil.getStringFromBundle("dataretrieverAPI.noMsgResultsFound");
if ((session.getUser() != null) && (session.getUser().isAuthenticated())) {
authUser = (AuthenticatedUser) session.getUser();
@@ -478,13 +479,14 @@ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @Qu
}
roleList = dataverseRoleService.findAll();
rolePermissionHelper = new DataverseRolePermissionHelper(roleList);
+ List roleIdList = Arrays.asList(1L, 3L, 5L, 7L);
// ---------------------------------
// (1) Initialize filterParams and check for Errors
// ---------------------------------
DataverseRequest dataverseRequest = createDataverseRequest(authUser);
- MyDataFilterParams filterParams = new MyDataFilterParams(dataverseRequest, Arrays.asList(DvObject.DType.Dataverse), MyDataFilterParams.allPublishedStates, Arrays.asList(1L, 3L, 5L, 7L), null, null);
+ MyDataFilterParams filterParams = new MyDataFilterParams(dataverseRequest, Arrays.asList(DvObject.DType.Dataverse), MyDataFilterParams.allPublishedStates, roleIdList, null, null);
if (filterParams.hasError()){
return this.getJSONErrorString(filterParams.getErrorMessage(), filterParams.getErrorMessage());
}
@@ -507,7 +509,6 @@ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @Qu
int paginationStart = 0;
int totalCount;
List collections = new ArrayList<>();
- JsonArrayBuilder jsonDataArray = Json.createArrayBuilder();
List filterQueries = this.myDataFinder.getSolrFilterQueries();
if (filterQueries==null){
@@ -544,11 +545,10 @@ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @Qu
solrQueryResponse = null;
logger.severe("Solr SearchException: " + ex.getMessage());
}
- for (Dataverse dv : collections) {
- jsonDataArray.add(JsonPrinter.json(dv));
- }
- return jsonDataArray.build().toString();
+ JsonArrayBuilder jsonArrayBuilder = JsonPrinter.json(collections, includeDescriptions);
+
+ return jsonArrayBuilder.build().toString();
}
private JsonObjectBuilder getDvObjectTypeCounts(SolrQueryResponse solrResponse) {
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 592a893083c..85bc117daa7 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
@@ -322,7 +322,19 @@ public static JsonObjectBuilder json(Dataverse dv, Boolean hideEmail, Boolean re
return bld;
}
-
+ public static JsonArrayBuilder json(List collections, Boolean includeDescription) {
+ JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
+ for (Dataverse dataverse : collections) {
+ NullSafeJsonBuilder jsonObject = NullSafeJsonBuilder.jsonObjectBuilder();
+ jsonObject.add("name", dataverse.getName());
+ jsonObject.add("alias", dataverse.getAlias());
+ if (includeDescription != null && includeDescription) {
+ jsonObject.add("description", dataverse.getDescription());
+ }
+ jsonArrayBuilder.add(jsonObject);
+ }
+ return jsonArrayBuilder;
+ }
public static JsonArrayBuilder json(List dataverseContacts) {
JsonArrayBuilder jsonArrayOfContacts = Json.createArrayBuilder();
for (DataverseContact dataverseContact : dataverseContacts) {
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
index 9e818d97b65..728e0f7d77a 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
@@ -18,6 +18,7 @@
import static jakarta.ws.rs.core.Response.Status.OK;
import static jakarta.ws.rs.core.Response.Status.UNAUTHORIZED;
import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DataRetrieverApiIT {
@@ -106,7 +107,7 @@ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
JsonPath jsonPath = retrieveMyDataAsJsonResponse.getBody().jsonPath();
assertEquals(2, jsonPath.getInt("data.total_count"));
- retrieveMyDataAsJsonResponse = UtilIT.retrieveMyCollectionList(normalUserApiToken, "");
+ retrieveMyDataAsJsonResponse = UtilIT.retrieveMyCollectionList(normalUserApiToken, null, null);
retrieveMyDataAsJsonResponse.prettyPrint();
// Clean up
@@ -137,40 +138,72 @@ public void testRetrieveMyDataCollections() throws InterruptedException {
String superUserApiToken = UtilIT.getApiTokenFromResponse(createUserResponse);
// Create User2
- Response createNormalUserResponse = UtilIT.createRandomUser();
- String normalUserUsername = UtilIT.getUsernameFromResponse(createNormalUserResponse);
- String normalUserApiToken = UtilIT.getApiTokenFromResponse(createNormalUserResponse);
+ createUserResponse = UtilIT.createRandomUser();
+ String User2Username = UtilIT.getUsernameFromResponse(createUserResponse);
+ String User2ApiToken = UtilIT.getApiTokenFromResponse(createUserResponse);
+ // Create User3
+ createUserResponse = UtilIT.createRandomUser();
+ String User3Username = UtilIT.getUsernameFromResponse(createUserResponse);
+ String User3ApiToken = UtilIT.getApiTokenFromResponse(createUserResponse);
// User1 creates a number of Dataverses and adds a role allowing User2 access
List dataverses = new ArrayList<>();
- for (int i = 0; i <15; i++) {
+ int user1DataverseCount = 15;
+ for (int i = 0; i {
Response deleteDataverseResponse = UtilIT.deleteDataverse(dv, superUserApiToken);
assertEquals(200, deleteDataverseResponse.getStatusCode());
});
- Response deleteUserResponse = UtilIT.deleteUser(normalUserUsername);
+ Response deleteUserResponse = UtilIT.deleteUser(User2Username);
+ deleteUserResponse.prettyPrint();
+ assertEquals(200, deleteUserResponse.getStatusCode());
+ deleteUserResponse = UtilIT.deleteUser(User3Username);
deleteUserResponse.prettyPrint();
assertEquals(200, deleteUserResponse.getStatusCode());
deleteUserResponse = UtilIT.deleteUser(superUserUsername);
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
index 8ff74625988..889785c4a83 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
@@ -408,6 +408,7 @@ static Response createSubDataverse(String alias, String category, String apiToke
JsonObjectBuilder objectBuilder = Json.createObjectBuilder()
.add("alias", alias)
.add("name", alias)
+ .add("description", "Description for " + alias)
.add("dataverseContacts", contactArrayBuilder)
.add("dataverseSubjects", subjectArrayBuilder)
// don't send "dataverseType" if category is null, must be a better way
@@ -4129,11 +4130,20 @@ static Response retrieveMyDataAsJsonString(String apiToken, String userIdentifie
.get("/api/mydata/retrieve?userIdentifier=" + userIdentifier);
return response;
}
- static Response retrieveMyCollectionList(String apiToken, String userIdentifier) {
- Response response = given()
- .header(API_TOKEN_HTTP_HEADER, apiToken)
- .get("/api/mydata/retrieve/collectionList?userIdentifier=" + userIdentifier);
- return response;
+
+ static Response retrieveMyCollectionList(String apiToken, String userIdentifier, Boolean includeDescriptions) {
+ RequestSpecification requestSpecification = given();
+ if (apiToken != null) {
+ requestSpecification.header(API_TOKEN_HTTP_HEADER, apiToken);
+ }
+ if (userIdentifier != null) {
+ requestSpecification.queryParam("userIdentifier", userIdentifier);
+ }
+ if (includeDescriptions != null) {
+ requestSpecification.queryParam("includeDescriptions", includeDescriptions);
+ }
+
+ return requestSpecification.get("/api/mydata/retrieve/collectionList");
}
static Response createSignedUrl(String apiToken, String apiPath, String username) {
From ec0bdcfab81d169e8090a4df84722833b899da1d Mon Sep 17 00:00:00 2001
From: Steven Winship <39765413+stevenwinship@users.noreply.github.com>
Date: Tue, 29 Jul 2025 10:15:15 -0400
Subject: [PATCH 07/30] switch to GetUserPermittedCollectionsCommand
---
.../GetUserPermittedCollectionsCommand.java | 23 +++-
.../iq/dataverse/mydata/DataRetrieverAPI.java | 89 ++------------
.../iq/dataverse/util/json/JsonPrinter.java | 10 +-
.../iq/dataverse/api/DataRetrieverApiIT.java | 116 ++++++++++--------
.../edu/harvard/iq/dataverse/api/UtilIT.java | 6 +-
5 files changed, 96 insertions(+), 148 deletions(-)
diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
index c4888c8c99c..499472df849 100644
--- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
+++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
@@ -3,7 +3,6 @@
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.authorization.Permission;
-import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.AbstractCommand;
import edu.harvard.iq.dataverse.engine.command.CommandContext;
@@ -18,6 +17,7 @@
import java.util.logging.Logger;
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.json;
+import static edu.harvard.iq.dataverse.util.json.JsonPrinter.jsonArray;
@RequiredPermissions({})
public class GetUserPermittedCollectionsCommand extends AbstractCommand {
@@ -26,11 +26,17 @@ public class GetUserPermittedCollectionsCommand extends AbstractCommand collections = ctxt.permissions().findPermittedCollections(request, user, permissionBit);
+
if (collections != null) {
JsonObjectBuilder job = Json.createObjectBuilder();
- JsonArrayBuilder jab = Json.createArrayBuilder();
- for (Dataverse dv : collections) {
- jab.add(json(dv));
- }
job.add("count", collections.size());
- job.add("items", jab);
+ if (minimalOutput != null && minimalOutput) {
+ job.add("items", jsonArray(collections));
+ } else {
+ JsonArrayBuilder jab = Json.createArrayBuilder();
+ for (Dataverse dv : collections) {
+ jab.add(json(dv));
+ }
+ job.add("items", jab);
+ }
return job;
}
return null;
diff --git a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
index 580c11e9863..beca1ebf9f0 100644
--- a/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
+++ b/src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java
@@ -5,6 +5,8 @@
import edu.harvard.iq.dataverse.*;
import edu.harvard.iq.dataverse.api.auth.AuthRequired;
+import edu.harvard.iq.dataverse.authorization.Permission;
+import edu.harvard.iq.dataverse.engine.command.impl.GetUserPermittedCollectionsCommand;
import edu.harvard.iq.dataverse.search.SolrQueryResponse;
import edu.harvard.iq.dataverse.search.SolrSearchResult;
import edu.harvard.iq.dataverse.api.AbstractApiBean;
@@ -23,7 +25,6 @@
import java.util.*;
import java.util.logging.Logger;
-import edu.harvard.iq.dataverse.util.json.JsonPrinter;
import jakarta.ejb.EJB;
import jakarta.inject.Inject;
import jakarta.json.*;
@@ -449,10 +450,8 @@ public String retrieveMyDataAsJsonString(
@GET
@AuthRequired
@Path(retrieveDataPartialAPIPath + "/collectionList")
- @Produces({"application/json"})
- public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @QueryParam("userIdentifier") String userIdentifier,
- @QueryParam("includeDescriptions") Boolean includeDescriptions) {
- String noMsgResultsFound = BundleUtil.getStringFromBundle("dataretrieverAPI.noMsgResultsFound");
+ @Produces("application/json")
+ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @QueryParam("userIdentifier") String userIdentifier) {
if ((session.getUser() != null) && (session.getUser().isAuthenticated())) {
authUser = (AuthenticatedUser) session.getUser();
} else {
@@ -466,89 +465,21 @@ public String retrieveMyCollectionList(@Context ContainerRequestContext crc, @Qu
}
}
// If the user is a superuser, see if a userIdentifier has been specified and use that instead
- AuthenticatedUser searchUser = null;
+ AuthenticatedUser searchUser = authUser;
if ((authUser.isSuperuser()) && (userIdentifier != null) && (!userIdentifier.isEmpty())) {
searchUser = getUserFromIdentifier(userIdentifier);
- if (searchUser != null) {
- authUser = searchUser;
- } else {
+ if (searchUser == null) {
return this.getJSONErrorString(
BundleUtil.getStringFromBundle("dataretrieverAPI.user.not.found", Arrays.asList(userIdentifier)),
null);
}
}
- roleList = dataverseRoleService.findAll();
- rolePermissionHelper = new DataverseRolePermissionHelper(roleList);
- List roleIdList = Arrays.asList(1L, 3L, 5L, 7L);
-
- // ---------------------------------
- // (1) Initialize filterParams and check for Errors
- // ---------------------------------
- DataverseRequest dataverseRequest = createDataverseRequest(authUser);
-
- MyDataFilterParams filterParams = new MyDataFilterParams(dataverseRequest, Arrays.asList(DvObject.DType.Dataverse), MyDataFilterParams.allPublishedStates, roleIdList, null, null);
- if (filterParams.hasError()){
- return this.getJSONErrorString(filterParams.getErrorMessage(), filterParams.getErrorMessage());
- }
-
- // ---------------------------------
- // (2) Initialize MyDataFinder and check for Errors
- // ---------------------------------
- myDataFinder = new MyDataFinder(rolePermissionHelper,
- roleAssigneeService,
- dvObjectServiceBean,
- groupService);
- this.myDataFinder.runFindDataSteps(filterParams);
- if (myDataFinder.hasError()){
- return this.getJSONErrorString(myDataFinder.getErrorMessage(), myDataFinder.getErrorMessage());
- }
-
- // ---------------------------------
- // (3) Make Solr Query
- // ---------------------------------
- int paginationStart = 0;
- int totalCount;
- List collections = new ArrayList<>();
-
- List filterQueries = this.myDataFinder.getSolrFilterQueries();
- if (filterQueries==null){
- logger.fine("No ids found for this search");
- return this.getJSONErrorString(noMsgResultsFound, null);
- }
try {
- do {
- solrQueryResponse = searchService.getDefaultSearchService().search(
- dataverseRequest,
- null, // subtree, default it to Dataverse for now
- filterParams.getSearchTerm(), //"*", //
- filterQueries,//filterQueries,
- SearchFields.RELEASE_OR_CREATE_DATE, SortBy.DESCENDING,
- paginationStart, //paginationStart,
- true, // dataRelatedToMe
- SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE //10 // SearchFields.NUM_SOLR_DOCS_TO_RETRIEVE
- );
- if (this.solrQueryResponse.getNumResultsFound() == 0) {
- return this.getJSONErrorString(noMsgResultsFound, null);
- }
- totalCount = solrQueryResponse.getNumResultsFound().intValue();
-
- for (SolrSearchResult result : solrQueryResponse.getSolrSearchResults()) {
- if (result.getEntity() instanceof Dataverse) {
- collections.add((Dataverse) result.getEntity());
- }
- paginationStart++;
- }
-
- } while (totalCount > paginationStart);
-
- } catch (SearchException ex) {
- solrQueryResponse = null;
- logger.severe("Solr SearchException: " + ex.getMessage());
+ JsonObjectBuilder jsonObjBuilder = execCommand(new GetUserPermittedCollectionsCommand(createDataverseRequest(getRequestUser(crc)), searchUser, Permission.AddDataset.name(), true));
+ return jsonObjBuilder.build().toString();
+ } catch (WrappedResponse ex) {
+ return null;
}
-
- JsonArrayBuilder jsonArrayBuilder = JsonPrinter.json(collections, includeDescriptions);
-
- return jsonArrayBuilder.build().toString();
}
private JsonObjectBuilder getDvObjectTypeCounts(SolrQueryResponse solrResponse) {
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 85bc117daa7..34c480d0e9b 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
@@ -322,19 +322,19 @@ public static JsonObjectBuilder json(Dataverse dv, Boolean hideEmail, Boolean re
return bld;
}
- public static JsonArrayBuilder json(List collections, Boolean includeDescription) {
+
+ // For UI drop down list. Only needing display name and identifier
+ public static JsonArrayBuilder jsonArray(List collections) {
JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
for (Dataverse dataverse : collections) {
NullSafeJsonBuilder jsonObject = NullSafeJsonBuilder.jsonObjectBuilder();
- jsonObject.add("name", dataverse.getName());
+ jsonObject.add("name", dataverse.getDisplayName());
jsonObject.add("alias", dataverse.getAlias());
- if (includeDescription != null && includeDescription) {
- jsonObject.add("description", dataverse.getDescription());
- }
jsonArrayBuilder.add(jsonObject);
}
return jsonArrayBuilder;
}
+
public static JsonArrayBuilder json(List dataverseContacts) {
JsonArrayBuilder jsonArrayOfContacts = Json.createArrayBuilder();
for (DataverseContact dataverseContact : dataverseContacts) {
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
index 728e0f7d77a..4c1d2777f49 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java
@@ -20,6 +20,7 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class DataRetrieverApiIT {
@@ -31,7 +32,7 @@ public static void setUpClass() {
}
@Test
- public void testRetrieveMyDataAsJsonString() throws InterruptedException {
+ public void testRetrieveMyDataAsJsonString() {
// Call with bad API token
ArrayList emptyRoleIdsList = new ArrayList<>();
Response badApiTokenResponse = UtilIT.retrieveMyDataAsJsonString("bad-token", "dummy-user-identifier", emptyRoleIdsList);
@@ -92,24 +93,6 @@ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
assertEquals(1, jsonPathOneDataverse.getInt("data.total_count"));
assertEquals(dataverseAlias, jsonPathOneDataverse.getString("data.items[0].name"));
- // Use retrieve mydata endpoint to get a list of collections that this user can add Datasets to
- createDataverseResponse = UtilIT.createRandomDataverse(superUserApiToken);
- createDataverseResponse.prettyPrint();
- String dataverseAlias2 = UtilIT.getAliasFromResponse(createDataverseResponse);
- // Add Curator role so this collection will show up in the list
- UtilIT.grantRoleOnDataverse(dataverseAlias2, DataverseRole.CURATOR.toString(),
- "@" + normalUserUsername, superUserApiToken);
- // Sleep for indexing
- Thread.sleep(4000);
-
- Response retrieveMyDataAsJsonResponse = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", Arrays.asList(1L, 3L , 5L, 7L), Arrays.asList(DvObject.DType.Dataverse));
- retrieveMyDataAsJsonResponse.prettyPrint();
- JsonPath jsonPath = retrieveMyDataAsJsonResponse.getBody().jsonPath();
- assertEquals(2, jsonPath.getInt("data.total_count"));
-
- retrieveMyDataAsJsonResponse = UtilIT.retrieveMyCollectionList(normalUserApiToken, null, null);
- retrieveMyDataAsJsonResponse.prettyPrint();
-
// Clean up
Response deleteDatasetResponse = UtilIT.deleteDatasetViaNativeApi(datasetId, normalUserApiToken);
deleteDatasetResponse.prettyPrint();
@@ -118,9 +101,6 @@ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
Response deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias, normalUserApiToken);
deleteDataverseResponse.prettyPrint();
assertEquals(200, deleteDataverseResponse.getStatusCode());
- deleteDataverseResponse = UtilIT.deleteDataverse(dataverseAlias2, superUserApiToken);
- deleteDataverseResponse.prettyPrint();
- assertEquals(200, deleteDataverseResponse.getStatusCode());
Response deleteUserResponse = UtilIT.deleteUser(normalUserUsername);
deleteUserResponse.prettyPrint();
@@ -130,13 +110,21 @@ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
// Test getting a list of collections that the user can add datasets to
@Test
public void testRetrieveMyDataCollections() throws InterruptedException {
- // Create User1
+ int rootCount = 1; // everyone has access to this dataverse
+ List
+ */
+@RequiredPermissions({})
public class GetUserPermittedCollectionsCommand extends AbstractCommand> {
private static final Logger logger = Logger.getLogger(GetUserPermittedCollectionsCommand.class.getCanonicalName());
From 2678e69ef6ed50737ffdc9c67965a3b449e1a499 Mon Sep 17 00:00:00 2001
From: GPortas
Date: Mon, 8 Sep 2025 15:44:50 +0100
Subject: [PATCH 28/30] Refactor: removed unused logger and applied final
keyword in class level variables
---
.../command/impl/GetUserPermittedCollectionsCommand.java | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
index 4cfa09d4ef2..62ade887a19 100644
--- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
+++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
@@ -11,7 +11,6 @@
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import java.util.List;
-import java.util.logging.Logger;
/**
* Command that retrieves all {@link Dataverse} collections for which a given
@@ -33,11 +32,10 @@
*/
@RequiredPermissions({})
public class GetUserPermittedCollectionsCommand extends AbstractCommand> {
- private static final Logger logger = Logger.getLogger(GetUserPermittedCollectionsCommand.class.getCanonicalName());
- private DataverseRequest request;
- private AuthenticatedUser user;
- private String permission;
+ private final DataverseRequest request;
+ private final AuthenticatedUser user;
+ private final String permission;
public GetUserPermittedCollectionsCommand(DataverseRequest request, AuthenticatedUser user, String permission) {
super(request, (DvObject) null);
From cf7482e61f7d2af108d3ea272290d4e908edb0e1 Mon Sep 17 00:00:00 2001
From: GPortas
Date: Mon, 8 Sep 2025 16:17:05 +0100
Subject: [PATCH 29/30] Added: unit tests for
GetUserPermittedCollectionsCommand
---
.../GetUserPermittedCollectionsCommand.java | 11 +-
src/main/java/propertyFiles/Bundle.properties | 4 +
...etUserPermittedCollectionsCommandTest.java | 135 ++++++++++++++++++
3 files changed, 146 insertions(+), 4 deletions(-)
create mode 100644 src/test/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommandTest.java
diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
index 62ade887a19..28a924bea92 100644
--- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
+++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommand.java
@@ -9,6 +9,8 @@
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.RequiredPermissions;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
+import edu.harvard.iq.dataverse.engine.command.exception.InvalidCommandArgumentsException;
+import edu.harvard.iq.dataverse.util.BundleUtil;
import java.util.List;
@@ -33,6 +35,8 @@
@RequiredPermissions({})
public class GetUserPermittedCollectionsCommand extends AbstractCommand> {
+ public static final String ANY_PERMISSION = "any";
+
private final DataverseRequest request;
private final AuthenticatedUser user;
private final String permission;
@@ -47,14 +51,13 @@ public GetUserPermittedCollectionsCommand(DataverseRequest request, Authenticate
@Override
public List execute(CommandContext ctxt) throws CommandException {
if (user == null) {
- throw new CommandException("User not found.", this);
+ throw new CommandException(BundleUtil.getStringFromBundle("getUserPermittedCollectionsCommand.errors.userNotFound"), this);
}
int permissionBit;
try {
- permissionBit = permission.equalsIgnoreCase("any") ?
- Integer.MAX_VALUE : (1 << Permission.valueOf(permission).ordinal());
+ permissionBit = permission.equalsIgnoreCase(ANY_PERMISSION) ? Integer.MAX_VALUE : (1 << Permission.valueOf(permission).ordinal());
} catch (IllegalArgumentException e) {
- throw new CommandException("Permission not valid.", this);
+ throw new InvalidCommandArgumentsException(BundleUtil.getStringFromBundle("getUserPermittedCollectionsCommand.errors.permissionNotValid"), this);
}
return ctxt.permissions().findPermittedCollections(request, user, permissionBit);
}
diff --git a/src/main/java/propertyFiles/Bundle.properties b/src/main/java/propertyFiles/Bundle.properties
index d527ba3eeeb..a07546284e0 100644
--- a/src/main/java/propertyFiles/Bundle.properties
+++ b/src/main/java/propertyFiles/Bundle.properties
@@ -3232,3 +3232,7 @@ abstractApiBean.error.internalVersionTimestampIsOutdated=Internal version timest
#RoleAssigneeServiceBean.java
roleAssigneeServiceBean.error.dataverseRequestCannotBeNull=DataverseRequest cannot be null.
+
+#GetUserPermittedCollectionsCommand.java
+getUserPermittedCollectionsCommand.errors.userNotFound=User not found.
+getUserPermittedCollectionsCommand.errors.permissionNotValid=Permission not valid.
diff --git a/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommandTest.java b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommandTest.java
new file mode 100644
index 00000000000..21b267be39c
--- /dev/null
+++ b/src/test/java/edu/harvard/iq/dataverse/engine/command/impl/GetUserPermittedCollectionsCommandTest.java
@@ -0,0 +1,135 @@
+package edu.harvard.iq.dataverse.engine.command.impl;
+
+import edu.harvard.iq.dataverse.Dataverse;
+import edu.harvard.iq.dataverse.PermissionServiceBean;
+import edu.harvard.iq.dataverse.authorization.Permission;
+import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
+import edu.harvard.iq.dataverse.engine.command.CommandContext;
+import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
+import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
+import edu.harvard.iq.dataverse.engine.command.exception.InvalidCommandArgumentsException;
+import edu.harvard.iq.dataverse.util.BundleUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class GetUserPermittedCollectionsCommandTest {
+
+ private DataverseRequest dataverseRequest;
+ private AuthenticatedUser authenticatedUser;
+ private CommandContext commandContext;
+ private PermissionServiceBean permissionsServiceBean;
+
+ @BeforeEach
+ public void setUp() {
+ dataverseRequest = Mockito.mock(DataverseRequest.class);
+ authenticatedUser = Mockito.mock(AuthenticatedUser.class);
+ commandContext = Mockito.mock(CommandContext.class);
+ permissionsServiceBean = Mockito.mock(PermissionServiceBean.class);
+ Mockito.when(commandContext.permissions()).thenReturn(permissionsServiceBean);
+ }
+
+ @Test
+ public void execute_shouldReturnCollections_whenAnyPermissionIsRequested() throws CommandException {
+ // Arrange
+ Dataverse dv1 = new Dataverse();
+ Dataverse dv2 = new Dataverse();
+ List expectedDataverses = Arrays.asList(dv1, dv2);
+
+ Mockito.when(permissionsServiceBean.findPermittedCollections(
+ Mockito.any(DataverseRequest.class),
+ Mockito.any(AuthenticatedUser.class),
+ Mockito.eq(Integer.MAX_VALUE)
+ )).thenReturn(expectedDataverses);
+
+ GetUserPermittedCollectionsCommand sut = new GetUserPermittedCollectionsCommand(
+ dataverseRequest,
+ authenticatedUser,
+ GetUserPermittedCollectionsCommand.ANY_PERMISSION
+ );
+
+ // Act
+ List result = sut.execute(commandContext);
+
+ // Assert
+ assertEquals(expectedDataverses.size(), result.size());
+ assertEquals(expectedDataverses, result);
+ Mockito.verify(permissionsServiceBean).findPermittedCollections(
+ dataverseRequest,
+ authenticatedUser,
+ Integer.MAX_VALUE
+ );
+ }
+
+ @Test
+ public void execute_shouldReturnCollections_whenSpecificPermissionIsRequested() throws CommandException {
+ // Arrange
+ Dataverse dv = new Dataverse();
+ List expectedDataverses = Collections.singletonList(dv);
+
+ Mockito.when(permissionsServiceBean.findPermittedCollections(
+ Mockito.any(DataverseRequest.class),
+ Mockito.any(AuthenticatedUser.class),
+ Mockito.eq(1 << Permission.AddDataset.ordinal())
+ )).thenReturn(expectedDataverses);
+
+ GetUserPermittedCollectionsCommand sut = new GetUserPermittedCollectionsCommand(
+ dataverseRequest,
+ authenticatedUser,
+ Permission.AddDataset.name()
+ );
+
+ // Act
+ List result = sut.execute(commandContext);
+
+ // Assert
+ assertEquals(expectedDataverses.size(), result.size());
+ assertEquals(expectedDataverses, result);
+ Mockito.verify(permissionsServiceBean).findPermittedCollections(
+ dataverseRequest,
+ authenticatedUser,
+ 1 << Permission.AddDataset.ordinal()
+ );
+ }
+
+ @Test
+ public void execute_shouldThrowException_whenUserIsNotFound() {
+ // Arrange
+ AuthenticatedUser nullUser = null;
+ GetUserPermittedCollectionsCommand sut = new GetUserPermittedCollectionsCommand(
+ dataverseRequest,
+ nullUser,
+ GetUserPermittedCollectionsCommand.ANY_PERMISSION
+ );
+
+ // Act & Assert
+ CommandException exception = assertThrows(CommandException.class, () -> {
+ sut.execute(commandContext);
+ });
+ assertEquals(BundleUtil.getStringFromBundle("getUserPermittedCollectionsCommand.errors.userNotFound"), exception.getMessage());
+ }
+
+ @Test
+ public void execute_shouldThrowException_whenPermissionIsNotValid() {
+ // Arrange
+ String invalidPermission = "invalid_permission_name";
+ GetUserPermittedCollectionsCommand sut = new GetUserPermittedCollectionsCommand(
+ dataverseRequest,
+ authenticatedUser,
+ invalidPermission
+ );
+
+ // Act & Assert
+ InvalidCommandArgumentsException exception = assertThrows(InvalidCommandArgumentsException.class, () -> {
+ sut.execute(commandContext);
+ });
+ assertEquals(BundleUtil.getStringFromBundle("getUserPermittedCollectionsCommand.errors.permissionNotValid"), exception.getMessage());
+ }
+}
From 801d8cc445c4c714490e6a15e64d0dbe6269f549 Mon Sep 17 00:00:00 2001
From: GPortas
Date: Mon, 8 Sep 2025 16:21:02 +0100
Subject: [PATCH 30/30] Fixed: incorrect response code in
testUserPermittedDataverses IT
---
src/test/java/edu/harvard/iq/dataverse/api/UsersIT.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/test/java/edu/harvard/iq/dataverse/api/UsersIT.java b/src/test/java/edu/harvard/iq/dataverse/api/UsersIT.java
index dce4871dc16..c7ee126193c 100644
--- a/src/test/java/edu/harvard/iq/dataverse/api/UsersIT.java
+++ b/src/test/java/edu/harvard/iq/dataverse/api/UsersIT.java
@@ -564,7 +564,7 @@ public void testUserPermittedDataverses() {
collectionsResp = UtilIT.getUserPermittedCollections("fakeUser", superuserApiToken, "ViewUnpublishedDataset");
assertEquals(500, collectionsResp.getStatusCode());
collectionsResp = UtilIT.getUserPermittedCollections(usernameOfUser, superuserApiToken, "bad");
- assertEquals(500, collectionsResp.getStatusCode());
+ assertEquals(BAD_REQUEST.getStatusCode(), collectionsResp.getStatusCode());
// Testing adding an explicit permission/role to one dataverse
collectionsResp = UtilIT.getUserPermittedCollections(usernameOfUser, userApiToken, "DownloadFile");