Skip to content

Commit a909602

Browse files
committed
return empty result set instead or error
1 parent 27e1f1f commit a909602

7 files changed

Lines changed: 94 additions & 47 deletions

File tree

doc/release-notes/11447.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Feature MyData API Endpoint - don't return error for empty result set
2+
3+
**GET /api/mydata/retrieve** will now return "data" block with 0 results if the result set is empty, Also, the "success" status will be returned as 'true' and the message giving context as to the 0 results will be returned in "message" instead of "error_message". All true errors will still return "success":false and "error_message":"Some error" with no "data" block.

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ This API changelog is experimental and we would love feedback on its usefulness.
77
:local:
88
:depth: 1
99

10+
v6.11
11+
-----
12+
13+
- The GET /api/mydata/retrieve, if the search returns no data, now includes the "data" block with 0 results. The message that was returned in "error_message" will be returned in "message" and the "success" will be `true`. All other errors will continue to reply with "success":false and the error message in "error_message".
14+
1015
v6.10
1116
-----
1217
- The following GET APIs will now return ``400`` if a required Guestbook Response is not supplied. A Guestbook Response can be passed to these APIs in the JSON body using a POST call. See the notes under :ref:`basic-file-access` and :ref:`download-by-dataset-by-version` for details.

src/main/java/edu/harvard/iq/dataverse/mydata/DataRetrieverAPI.java

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public class DataRetrieverAPI extends AbstractApiBean {
8484

8585
public static final String JSON_SUCCESS_FIELD_NAME = "success";
8686
public static final String JSON_ERROR_MSG_FIELD_NAME = "error_message";
87+
public static final String JSON_MSG_FIELD_NAME = "message";
8788
public static final String JSON_DATA_FIELD_NAME = "data";
8889

8990
/**
@@ -190,26 +191,18 @@ public String retrieveMyDataAsJsonString(
190191
}
191192

192193
// ---------------------------------
193-
// (1) Initialize filterParams and check for Errors
194+
// (1) Initialize filterParams and MyDataFinder and check for Errors
194195
// ---------------------------------
195196
DataverseRequest dataverseRequest = createDataverseRequest(authUser);
196-
197-
198197
MyDataFilterParams filterParams = new MyDataFilterParams(dataverseRequest, dtypes, pub_states, roleIds, searchTerm, validities);
199-
if (filterParams.hasError()){
200-
return this.getJSONErrorString(filterParams.getErrorMessage(), filterParams.getErrorMessage());
198+
myDataFinder = new MyDataFinder(rolePermissionHelper, roleAssigneeService, dvObjectServiceBean, groupService);
199+
myDataFinder.runFindDataSteps(filterParams);
200+
201+
if (filterParams.hasError()) {
202+
return myDataAsJson(filterParams.getErrorMessage()).build().toString();
201203
}
202-
203-
// ---------------------------------
204-
// (2) Initialize MyDataFinder and check for Errors
205-
// ---------------------------------
206-
myDataFinder = new MyDataFinder(rolePermissionHelper,
207-
roleAssigneeService,
208-
dvObjectServiceBean,
209-
groupService);
210-
this.myDataFinder.runFindDataSteps(filterParams);
211-
if (myDataFinder.hasError()){
212-
return this.getJSONErrorString(myDataFinder.getErrorMessage(), myDataFinder.getErrorMessage());
204+
if (myDataFinder.hasError()) {
205+
return myDataAsJson(myDataFinder.getErrorMessage()).build().toString();
213206
}
214207

215208
// ---------------------------------
@@ -226,7 +219,7 @@ public String retrieveMyDataAsJsonString(
226219
List<String> defaultFilterQueries = this.myDataFinder.getSolrFilterQueries();
227220
if (defaultFilterQueries==null){
228221
logger.fine("No ids found for this search");
229-
return this.getJSONErrorString(noMsgResultsFound, null);
222+
return myDataAsJson(noMsgResultsFound).build().toString();
230223
}
231224
filterQueries.addAll(defaultFilterQueries);
232225

@@ -257,7 +250,7 @@ public String retrieveMyDataAsJsonString(
257250
);
258251

259252
if (this.solrQueryResponse.getNumResultsFound()==0){
260-
return this.getJSONErrorString(noMsgResultsFound, null);
253+
return myDataAsJson(noMsgResultsFound).build().toString();
261254
}
262255

263256
} catch (SearchException ex) {
@@ -281,30 +274,14 @@ public String retrieveMyDataAsJsonString(
281274
// - DvObject counts
282275
// ---------------------------------
283276

284-
// Initialize JSON response
285-
JsonObjectBuilder jsonData = Json.createObjectBuilder();
286-
287277
Pager pager = new Pager(solrQueryResponse.getNumResultsFound().intValue(),
288278
SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE,
289279
paginationStart);
290280

291281
RoleTagRetriever roleTagRetriever = new RoleTagRetriever(this.rolePermissionHelper, this.roleAssigneeSvc, this.dvObjectServiceBean);
292282
roleTagRetriever.loadRoles(dataverseRequest, solrQueryResponse);
293283

294-
295-
jsonData.add(DataRetrieverAPI.JSON_SUCCESS_FIELD_NAME, true)
296-
.add(DataRetrieverAPI.JSON_DATA_FIELD_NAME,
297-
Json.createObjectBuilder()
298-
.add("pagination", pager.asJsonObjectBuilderUsingCardTerms())
299-
//.add(SearchConstants.SEARCH_API_ITEMS, this.formatSolrDocs(solrQueryResponse, filterParams, this.myDataFinder))
300-
.add(SearchConstants.SEARCH_API_ITEMS, this.formatSolrDocs(solrQueryResponse, roleTagRetriever, metadataFields))
301-
.add(SearchConstants.SEARCH_API_TOTAL_COUNT, solrQueryResponse.getNumResultsFound())
302-
.add(SearchConstants.SEARCH_API_START, solrQueryResponse.getResultsStart())
303-
.add("search_term", filterParams.getSearchTerm())
304-
.add("dvobject_counts", this.getDvObjectTypeCounts(solrQueryResponse))
305-
.add("pubstatus_counts", this.getPublicationStatusCounts(solrQueryResponse))
306-
.add("selected_filters", this.myDataFinder.getSelectedFilterParamsAsJSON())
307-
);
284+
JsonObjectBuilder jsonData = myDataAsJson(null, pager, roleTagRetriever, metadataFields);
308285

309286
// ---------------------------------------------------------
310287
// We're doing ~another~ solr query here
@@ -320,6 +297,32 @@ public String retrieveMyDataAsJsonString(
320297
return jsonData.build().toString();
321298
}
322299

300+
// For empty data to prevent null pointer exceptions in all the dependencies
301+
private JsonObjectBuilder myDataAsJson(String message) {
302+
solrQueryResponse = new SolrQueryResponse(null);
303+
return myDataAsJson(message, new Pager(0, SearchConstants.NUM_SOLR_DOCS_TO_RETRIEVE, 1),
304+
new RoleTagRetriever(this.rolePermissionHelper, this.roleAssigneeSvc, this.dvObjectServiceBean), List.of());
305+
}
306+
307+
private JsonObjectBuilder myDataAsJson(String message, Pager pager, RoleTagRetriever roleTagRetriever, List<String> metadataFields) {
308+
JsonObjectBuilder jsonData = Json.createObjectBuilder().add(DataRetrieverAPI.JSON_SUCCESS_FIELD_NAME, true);
309+
if (message != null) {
310+
jsonData.add(DataRetrieverAPI.JSON_MSG_FIELD_NAME, message);
311+
}
312+
jsonData.add(DataRetrieverAPI.JSON_DATA_FIELD_NAME,
313+
Json.createObjectBuilder()
314+
.add("pagination", pager.asJsonObjectBuilderUsingCardTerms())
315+
.add(SearchConstants.SEARCH_API_ITEMS, this.formatSolrDocs(solrQueryResponse, roleTagRetriever, metadataFields))
316+
.add(SearchConstants.SEARCH_API_TOTAL_COUNT, solrQueryResponse.getNumResultsFound())
317+
.add(SearchConstants.SEARCH_API_START, solrQueryResponse.getResultsStart())
318+
.add("search_term", myDataFinder.filterParams.getSearchTerm())
319+
.add("dvobject_counts", this.getDvObjectTypeCounts(solrQueryResponse))
320+
.add("pubstatus_counts", this.getPublicationStatusCounts(solrQueryResponse))
321+
.add("selected_filters", this.myDataFinder.getSelectedFilterParamsAsJSON())
322+
);
323+
return jsonData;
324+
}
325+
323326
@GET
324327
@AuthRequired
325328
@Path(retrieveDataPartialAPIPath + "/collectionList")

src/main/java/edu/harvard/iq/dataverse/mydata/MyDataFinder.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,10 @@ public JsonArrayBuilder getListofSelectedRoles(){
424424
JsonArrayBuilder jsonArray = Json.createArrayBuilder();
425425

426426
for (Long roleId : this.filterParams.getRoleIds()){
427-
jsonArray.add(this.rolePermissionHelper.getRoleName(roleId));
427+
String roleName = this.rolePermissionHelper.getRoleName(roleId);
428+
if (roleName != null) {
429+
jsonArray.add(roleName);
430+
}
428431
}
429432
return jsonArray;
430433
}

src/main/java/edu/harvard/iq/dataverse/search/SolrQueryResponse.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ public class SolrQueryResponse {
1414

1515
private static final Logger logger = Logger.getLogger(SolrQueryResponse.class.getCanonicalName());
1616

17-
private List<SolrSearchResult> solrSearchResults;
18-
private Long numResultsFound;
19-
private Long resultsStart;
17+
private List<SolrSearchResult> solrSearchResults = List.of();
18+
private Long numResultsFound = 0L;
19+
private Long resultsStart = 0L;
2020
private Map<String, List<String>> spellingSuggestionsByToken;
2121
private List<FacetCategory> facetCategoryList;
2222
private List<FacetCategory> typeFacetCategories;

src/main/webapp/resources/js/mydata.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,11 @@ function submit_my_data_search(){
424424
$('#ajaxStatusPanel_start').hide();
425425
return;
426426
}
427+
if (data.message != null){
428+
setWarningAlert(data.message);
429+
$('#ajaxStatusPanel_start').hide();
430+
return;
431+
}
427432

428433
// --------------------------------
429434
// (2b) Looks good, let's make page

src/test/java/edu/harvard/iq/dataverse/api/DataRetrieverApiIT.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
public class DataRetrieverApiIT {
2626

2727
private static final String ERR_MSG_FORMAT = "{\n \"success\": false,\n \"error_message\": \"%s\"\n}";
28+
private static final String MSG_FORMAT = "{\n \"success\": true,\n \"message\": \"%s\"\n}";
2829

2930
@BeforeAll
3031
public static void setUpClass() {
@@ -53,20 +54,26 @@ public void testRetrieveMyDataAsJsonString() throws InterruptedException {
5354
Response createSecondUserResponse = UtilIT.createRandomUser();
5455
String userIdentifier = UtilIT.getUsernameFromResponse(createSecondUserResponse);
5556
Response validUserIdentifierResponse = UtilIT.retrieveMyDataAsJsonString(superUserApiToken, userIdentifier, emptyRoleIdsList);
56-
assertEquals(prettyPrintError("myDataFinder.error.result.no.role", null), validUserIdentifierResponse.prettyPrint());
57+
String resp = validUserIdentifierResponse.prettyPrint();
58+
assertTrue(resp.contains("\"success\": true"));
59+
assertTrue(resp.contains(prettyPrintMessage("myDataFinder.error.result.no.role", null)));
5760
assertEquals(OK.getStatusCode(), validUserIdentifierResponse.getStatusCode());
5861

5962
// Call as normal user with one valid role and no results
6063
Response createNormalUserResponse = UtilIT.createRandomUser();
6164
String normalUserUsername = UtilIT.getUsernameFromResponse(createNormalUserResponse);
6265
String normalUserApiToken = UtilIT.getApiTokenFromResponse(createNormalUserResponse);
6366
Response noResultwithOneRoleResponse = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", new ArrayList<>(Arrays.asList(5L)));
64-
assertEquals(prettyPrintError("myDataFinder.error.result.role.empty", Arrays.asList("Dataset Creator")), noResultwithOneRoleResponse.prettyPrint());
67+
resp = noResultwithOneRoleResponse.prettyPrint();
68+
assertTrue(resp.contains("\"success\": true"));
69+
assertTrue(resp.contains(prettyPrintMessage("myDataFinder.error.result.role.empty", Arrays.asList("Dataset Creator"))));
6570
assertEquals(OK.getStatusCode(), noResultwithOneRoleResponse.getStatusCode());
6671

6772
// Call as normal user with multiple valid roles and no results
6873
Response noResultWithMultipleRoleResponse = UtilIT.retrieveMyDataAsJsonString(normalUserApiToken, "", new ArrayList<>(Arrays.asList(5L, 6L)));
69-
assertEquals(prettyPrintError("myDataFinder.error.result.roles.empty", Arrays.asList("Dataset Creator, Contributor")), noResultWithMultipleRoleResponse.prettyPrint());
74+
resp = noResultWithMultipleRoleResponse.prettyPrint();
75+
assertTrue(resp.contains("\"success\": true"));
76+
assertTrue(resp.contains(prettyPrintMessage("myDataFinder.error.result.roles.empty", Arrays.asList("Dataset Creator, Contributor"))));
7077
assertEquals(OK.getStatusCode(), noResultWithMultipleRoleResponse.getStatusCode());
7178

7279
// Call as normal user with one valid dataset role and one dataset result
@@ -252,7 +259,10 @@ public void testRetrieveMyDataAsJsonStringSortOrder() {
252259

253260
// Call as regular user with no result
254261
Response myDataEmptyResponse = UtilIT.retrieveMyDataAsJsonString(userApiToken, "", new ArrayList<>(Arrays.asList(6L)));
255-
assertEquals(prettyPrintError("myDataFinder.error.result.role.empty", Arrays.asList("Contributor")), myDataEmptyResponse.prettyPrint());
262+
//assertEquals(prettyPrintError("myDataFinder.error.result.role.empty", Arrays.asList("Contributor")), myDataEmptyResponse.prettyPrint());
263+
String resp = myDataEmptyResponse.prettyPrint();
264+
assertTrue(resp.contains("\"success\": true"));
265+
assertTrue(resp.contains(prettyPrintMessage("myDataFinder.error.result.role.empty", Arrays.asList("Contributor"))));
256266
assertEquals(OK.getStatusCode(), myDataEmptyResponse.getStatusCode());
257267

258268
// Create and publish a dataverse
@@ -317,7 +327,7 @@ public void testRetrieveMyDataAsJsonStringSortOrder() {
317327
assertEquals("RELEASED", jsonPathTwoPublishedDatasets.getString("data.items[1].versionState"));
318328

319329
// Create new draft version of dataset 1 by updating metadata
320-
String pathToJsonFilePostPub= "doc/sphinx-guides/source/_static/api/dataset-add-metadata-after-pub.json";
330+
String pathToJsonFilePostPub = "doc/sphinx-guides/source/_static/api/dataset-add-metadata-after-pub.json";
321331
Response addDataToPublishedVersion = UtilIT.addDatasetMetadataViaNative(datasetOnePid, pathToJsonFilePostPub, userApiToken);
322332
addDataToPublishedVersion.prettyPrint();
323333
addDataToPublishedVersion.then().assertThat().statusCode(OK.getStatusCode());
@@ -423,9 +433,9 @@ public void testRetrieveMyDataWithMetadataFields() {
423433

424434
Response createDatasetResponse = UtilIT.createRandomDatasetViaNativeApi(dataverseAlias, apiToken);
425435
String datasetId = UtilIT.getDatasetIdFromResponse(createDatasetResponse).toString();
426-
436+
427437
UtilIT.sleepForReindex(datasetId, apiToken, 5);
428-
438+
429439
Response myDataWithAuthor = UtilIT.retrieveMyDataAsJsonString(apiToken, "", new ArrayList<>(Arrays.asList(6L)), "&metadata_fields=citation:author");
430440
myDataWithAuthor.prettyPrint();
431441
myDataWithAuthor.then().assertThat()
@@ -478,7 +488,7 @@ public void testRetrieveMyDataWithCollections() {
478488
UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken).then().assertThat().statusCode(OK.getStatusCode());
479489

480490
UtilIT.sleepForReindex(datasetPid, apiToken, 5);
481-
491+
482492
// Test that the Dataverse collection that the dataset was created in is returned
483493
Response myDataResponse = UtilIT.retrieveMyDataAsJsonString(apiToken, "", new ArrayList<>(Arrays.asList(6L)), "&show_collections=true");
484494
myDataResponse.prettyPrint();
@@ -525,4 +535,22 @@ private static String prettyPrintError(String resourceBundleKey, List<String> pa
525535
}
526536
return String.format(ERR_MSG_FORMAT, errorMessage.replaceAll("\"", "\\\\\""));
527537
}
538+
private static String prettyPrintMessage(String resourceBundleKey, List<String> params) {
539+
final String message;
540+
if (params == null || params.isEmpty()) {
541+
message = BundleUtil.getStringFromBundle(resourceBundleKey);
542+
} else {
543+
message = BundleUtil.getStringFromBundle(resourceBundleKey, params);
544+
}
545+
return message.replaceAll("\"", "\\\\\"");
546+
}
547+
548+
@Test
549+
public void testRetrieveMyDataAsJsonString2() {
550+
String apiToken = "3db5d5ce-ad6c-4501-ab42-8781cfea0829";
551+
552+
// Call with bad API token
553+
Response response = UtilIT.retrieveMyDataAsJsonString(apiToken, "", new ArrayList<>(Arrays.asList(0L)));
554+
response.prettyPrint();
555+
}
528556
}

0 commit comments

Comments
 (0)