Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix metadata field type display condition in dataverses/{id}/metadatablocks API endpoint #10642

Merged
merged 8 commits into from
Jul 2, 2024
2 changes: 2 additions & 0 deletions doc/release-notes/10637-fix-dataverse-metadatablocks-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dataverses/{id}/metadatablocks API endpoint has been fixed, since the fields returned for each metadata block when returnDatasetTypes query parameter is set to true was not correct.

18 changes: 12 additions & 6 deletions src/main/java/edu/harvard/iq/dataverse/Dataverse.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,12 +412,18 @@ public List<DataverseFieldTypeInputLevel> getDataverseFieldTypeInputLevels() {
}

public boolean isDatasetFieldTypeRequiredAsInputLevel(Long datasetFieldTypeId) {
for(DataverseFieldTypeInputLevel dataverseFieldTypeInputLevel : dataverseFieldTypeInputLevels) {
if (dataverseFieldTypeInputLevel.getDatasetFieldType().getId().equals(datasetFieldTypeId) && dataverseFieldTypeInputLevel.isRequired()) {
return true;
}
}
return false;
return dataverseFieldTypeInputLevels.stream()
.anyMatch(inputLevel -> inputLevel.getDatasetFieldType().getId().equals(datasetFieldTypeId) && inputLevel.isRequired());
}

public boolean isDatasetFieldTypeIncludedAsInputLevel(Long datasetFieldTypeId) {
return dataverseFieldTypeInputLevels.stream()
.anyMatch(inputLevel -> inputLevel.getDatasetFieldType().getId().equals(datasetFieldTypeId) && inputLevel.isInclude());
}

public boolean isDatasetFieldTypeInInputLevels(Long datasetFieldTypeId) {
qqmyers marked this conversation as resolved.
Show resolved Hide resolved
return dataverseFieldTypeInputLevels.stream()
.anyMatch(inputLevel -> inputLevel.getDatasetFieldType().getId().equals(datasetFieldTypeId));
}

public Template getDefaultTemplate() {
Expand Down
18 changes: 13 additions & 5 deletions src/main/java/edu/harvard/iq/dataverse/util/json/JsonPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -640,18 +640,26 @@ public static JsonObjectBuilder json(MetadataBlock metadataBlock, boolean printO

JsonObjectBuilder fieldsBuilder = Json.createObjectBuilder();
Set<DatasetFieldType> datasetFieldTypes = new TreeSet<>(metadataBlock.getDatasetFieldTypes());

for (DatasetFieldType datasetFieldType : datasetFieldTypes) {
boolean requiredInOwnerDataverse = ownerDataverse != null && ownerDataverse.isDatasetFieldTypeRequiredAsInputLevel(datasetFieldType.getId());
boolean displayCondition = !printOnlyDisplayedOnCreateDatasetFieldTypes ||
datasetFieldType.isDisplayOnCreate() ||
requiredInOwnerDataverse;
Long datasetFieldTypeId = datasetFieldType.getId();
boolean requiredAsInputLevelInOwnerDataverse = ownerDataverse != null && ownerDataverse.isDatasetFieldTypeRequiredAsInputLevel(datasetFieldTypeId);
boolean includedAsInputLevelInOwnerDataverse = ownerDataverse != null && ownerDataverse.isDatasetFieldTypeIncludedAsInputLevel(datasetFieldTypeId);
boolean isNotInputLevelInOwnerDataverse = ownerDataverse != null && !ownerDataverse.isDatasetFieldTypeInInputLevels(datasetFieldTypeId);

DatasetFieldType parentDatasetFieldType = datasetFieldType.getParentDatasetFieldType();
boolean isRequired = parentDatasetFieldType == null ? datasetFieldType.isRequired() : parentDatasetFieldType.isRequired();

boolean displayCondition = printOnlyDisplayedOnCreateDatasetFieldTypes
? (datasetFieldType.isDisplayOnCreate() || isRequired || requiredAsInputLevelInOwnerDataverse)
: ownerDataverse == null || includedAsInputLevelInOwnerDataverse || isNotInputLevelInOwnerDataverse;

if (displayCondition) {
fieldsBuilder.add(datasetFieldType.getName(), json(datasetFieldType, ownerDataverse));
}
}

jsonObjectBuilder.add("fields", fieldsBuilder);

return jsonObjectBuilder;
}

Expand Down
52 changes: 37 additions & 15 deletions src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -702,8 +702,10 @@ public void testListMetadataBlocks() {
Response setMetadataBlocksResponse = UtilIT.setMetadataBlocks(dataverseAlias, Json.createArrayBuilder().add("citation").add("astrophysics"), apiToken);
setMetadataBlocksResponse.then().assertThat().statusCode(OK.getStatusCode());

String[] testInputLevelNames = {"geographicCoverage", "country"};
Response updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, apiToken);
String[] testInputLevelNames = {"geographicCoverage", "country", "city"};
boolean[] testRequiredInputLevels = {false, true, false};
boolean[] testIncludedInputLevels = {false, true, true};
Response updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, testRequiredInputLevels, testIncludedInputLevels, apiToken);
updateDataverseInputLevelsResponse.then().assertThat().statusCode(OK.getStatusCode());

// Dataverse not found
Expand Down Expand Up @@ -769,6 +771,21 @@ public void testListMetadataBlocks() {
assertThat(expectedAllMetadataBlockDisplayNames, hasItemInArray(actualMetadataBlockDisplayName2));
assertThat(expectedAllMetadataBlockDisplayNames, hasItemInArray(actualMetadataBlockDisplayName3));

// Check dataset fields for the updated input levels are retrieved
int geospatialMetadataBlockIndex = actualMetadataBlockDisplayName1.equals("Geospatial Metadata") ? 0 : actualMetadataBlockDisplayName2.equals("Geospatial Metadata") ? 1 : 2;

// Since the included property of geographicCoverage is set to false, we should retrieve the total number of fields minus one
listMetadataBlocksResponse.then().assertThat()
.body(String.format("data[%d].fields.size()", geospatialMetadataBlockIndex), equalTo(10));

String actualMetadataField1 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.geographicCoverage.name", geospatialMetadataBlockIndex));
String actualMetadataField2 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.country.name", geospatialMetadataBlockIndex));
String actualMetadataField3 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.city.name", geospatialMetadataBlockIndex));

assertNull(actualMetadataField1);
assertNotNull(actualMetadataField2);
assertNotNull(actualMetadataField3);

// Existent dataverse and onlyDisplayedOnCreate=true and returnDatasetFieldTypes=true
listMetadataBlocksResponse = UtilIT.listMetadataBlocks(dataverseAlias, true, true, apiToken);
listMetadataBlocksResponse.then().assertThat().statusCode(OK.getStatusCode());
Expand All @@ -785,16 +802,18 @@ public void testListMetadataBlocks() {
assertThat(expectedOnlyDisplayedOnCreateMetadataBlockDisplayNames, hasItemInArray(actualMetadataBlockDisplayName2));

// Check dataset fields for the updated input levels are retrieved
int geospatialMetadataBlockIndex = actualMetadataBlockDisplayName2.equals("Geospatial Metadata") ? 1 : 0;
geospatialMetadataBlockIndex = actualMetadataBlockDisplayName2.equals("Geospatial Metadata") ? 1 : 0;

listMetadataBlocksResponse.then().assertThat()
.body(String.format("data[%d].fields.size()", geospatialMetadataBlockIndex), equalTo(2));
.body(String.format("data[%d].fields.size()", geospatialMetadataBlockIndex), equalTo(1));

String actualMetadataField1 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.geographicCoverage.name", geospatialMetadataBlockIndex));
String actualMetadataField2 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.country.name", geospatialMetadataBlockIndex));
actualMetadataField1 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.geographicCoverage.name", geospatialMetadataBlockIndex));
actualMetadataField2 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.country.name", geospatialMetadataBlockIndex));
actualMetadataField3 = listMetadataBlocksResponse.then().extract().path(String.format("data[%d].fields.city.name", geospatialMetadataBlockIndex));

assertNotNull(actualMetadataField1);
assertNull(actualMetadataField1);
assertNotNull(actualMetadataField2);
assertNull(actualMetadataField3);

// User has no permissions on the requested dataverse
Response createSecondUserResponse = UtilIT.createRandomUser();
Expand Down Expand Up @@ -898,12 +917,16 @@ public void testUpdateInputLevels() {

// Update valid input levels
String[] testInputLevelNames = {"geographicCoverage", "country"};
Response updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, apiToken);
boolean[] testRequiredInputLevels = {true, false};
boolean[] testIncludedInputLevels = {true, false};
Response updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, testRequiredInputLevels, testIncludedInputLevels, apiToken);
String actualInputLevelName = updateDataverseInputLevelsResponse.then().extract().path("data.inputLevels[0].datasetFieldTypeName");
int geographicCoverageInputLevelIndex = actualInputLevelName.equals("geographicCoverage") ? 0 : 1;
updateDataverseInputLevelsResponse.then().assertThat()
.body("data.inputLevels[0].required", equalTo(true))
.body("data.inputLevels[0].include", equalTo(true))
.body("data.inputLevels[1].required", equalTo(true))
.body("data.inputLevels[1].include", equalTo(true))
.body(String.format("data.inputLevels[%d].include", geographicCoverageInputLevelIndex), equalTo(true))
.body(String.format("data.inputLevels[%d].required", geographicCoverageInputLevelIndex), equalTo(true))
.body(String.format("data.inputLevels[%d].include", 1 - geographicCoverageInputLevelIndex), equalTo(false))
.body(String.format("data.inputLevels[%d].required", 1 - geographicCoverageInputLevelIndex), equalTo(false))
.statusCode(OK.getStatusCode());
String actualFieldTypeName1 = updateDataverseInputLevelsResponse.then().extract().path("data.inputLevels[0].datasetFieldTypeName");
String actualFieldTypeName2 = updateDataverseInputLevelsResponse.then().extract().path("data.inputLevels[1].datasetFieldTypeName");
Expand All @@ -913,15 +936,14 @@ public void testUpdateInputLevels() {

// Update input levels with an invalid field type name
String[] testInvalidInputLevelNames = {"geographicCoverage", "invalid1"};
updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInvalidInputLevelNames, apiToken);
updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInvalidInputLevelNames, testRequiredInputLevels, testIncludedInputLevels, apiToken);
updateDataverseInputLevelsResponse.then().assertThat()
.body("message", equalTo("Invalid dataset field type name: invalid1"))
.statusCode(BAD_REQUEST.getStatusCode());

// Update invalid empty input levels
testInputLevelNames = new String[]{};
updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, apiToken);
updateDataverseInputLevelsResponse.prettyPrint();
updateDataverseInputLevelsResponse = UtilIT.updateDataverseInputLevels(dataverseAlias, testInputLevelNames, testRequiredInputLevels, testIncludedInputLevels, apiToken);
updateDataverseInputLevelsResponse.then().assertThat()
.body("message", equalTo("Error while updating dataverse input levels: Input level list cannot be null or empty"))
.statusCode(INTERNAL_SERVER_ERROR.getStatusCode());
Expand Down
21 changes: 12 additions & 9 deletions src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -3953,22 +3953,25 @@ static Response requestGlobusUploadPaths(Integer datasetId, JsonObject body, Str
.post("/api/datasets/" + datasetId + "/requestGlobusUploadPaths");
}

static Response updateDataverseInputLevels(String dataverseAlias, String[] inputLevelNames, String apiToken) {
JsonArrayBuilder contactArrayBuilder = Json.createArrayBuilder();
for(String inputLevelName : inputLevelNames) {
contactArrayBuilder.add(Json.createObjectBuilder()
.add("datasetFieldTypeName", inputLevelName)
.add("required", true)
.add("include", true)
);
public static Response updateDataverseInputLevels(String dataverseAlias, String[] inputLevelNames, boolean[] requiredInputLevels, boolean[] includedInputLevels, String apiToken) {
JsonArrayBuilder inputLevelsArrayBuilder = Json.createArrayBuilder();
for (int i = 0; i < inputLevelNames.length; i++) {
inputLevelsArrayBuilder.add(createInputLevelObject(inputLevelNames[i], requiredInputLevels[i], includedInputLevels[i]));
}
return given()
.header(API_TOKEN_HTTP_HEADER, apiToken)
.body(contactArrayBuilder.build().toString())
.body(inputLevelsArrayBuilder.build().toString())
.contentType(ContentType.JSON)
.put("/api/dataverses/" + dataverseAlias + "/inputLevels");
}

private static JsonObjectBuilder createInputLevelObject(String name, boolean required, boolean include) {
return Json.createObjectBuilder()
.add("datasetFieldTypeName", name)
.add("required", required)
.add("include", include);
}

public static Response getOpenAPI(String accept, String format) {
Response response = given()
.header("Accept", accept)
Expand Down
Loading