Skip to content

Commit

Permalink
Merge pull request #3373 from IQSS/sbgrid-json-migration
Browse files Browse the repository at this point in the history
Allow category of dataverse to be set via native API (using JSON)
  • Loading branch information
kcondon authored Oct 14, 2016
2 parents 6e1171b + 8f1dd62 commit 8ad3431
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 100 deletions.
15 changes: 15 additions & 0 deletions doc/sphinx-guides/source/_static/api/dataverse-complete.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "Scientific Research",
"alias": "science",
"dataverseContacts": [
{
"contactEmail": "pi@example.edu"
},
{
"contactEmail": "student@example.edu"
}
],
"affiliation": "Scientific Research University",
"description": "We do all the science.",
"dataverseType": "LABORATORY"
}
9 changes: 9 additions & 0 deletions doc/sphinx-guides/source/_static/api/dataverse-minimal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Scientific Research",
"alias": "science",
"dataverseContacts": [
{
"contactEmail": "pi@example.edu"
}
]
}
15 changes: 14 additions & 1 deletion doc/sphinx-guides/source/api/native-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,24 @@ Endpoints

Dataverses
~~~~~~~~~~~
Generates a new dataverse under ``$id``. Expects a json content describing the dataverse.
Generates a new dataverse under ``$id``. Expects a JSON content describing the dataverse, as in the example below.
If ``$id`` is omitted, a root dataverse is created. ``$id`` can either be a dataverse id (long) or a dataverse alias (more robust). ::

POST http://$SERVER/api/dataverses/$id?key=$apiKey

The following JSON example can be `downloaded <../_static/api/dataverse-complete.json>`_ and modified to create dataverses to suit your needs. The fields ``name``, ``alias``, and ``dataverseContacts`` are required. The controlled vocabulary for ``dataverseType`` is

- ``JOURNALS``
- ``LABORATORY``
- ``ORGANIZATIONS_INSTITUTIONS``
- ``RESEARCHERS``
- ``RESEARCH_GROUP``
- ``RESEARCH_PROJECTS``
- ``TEACHING_COURSES``
- ``UNCATEGORIZED``

.. literalinclude:: ../_static/api/dataverse-complete.json

View data about the dataverse identified by ``$id``. ``$id`` can be the id number of the dataverse, its alias, or the special value ``:root``. ::

GET http://$SERVER/api/dataverses/$id
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,25 @@ public Response addDataverse( String body, @PathParam("identifier") String paren
d = execCommand( new CreateDataverseCommand(d, createDataverseRequest(u), null, null) );
return createdResponse( "/dataverses/"+d.getAlias(), json(d) );
} catch ( WrappedResponse ww ) {
Throwable cause = ww.getCause();
StringBuilder sb = new StringBuilder();
while (cause.getCause() != null) {
cause = cause.getCause();
if (cause instanceof ConstraintViolationException) {
ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause;
for (ConstraintViolation<?> violation : constraintViolationException.getConstraintViolations()) {
sb.append(" Invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ")
.append(violation.getPropertyPath()).append(" at ")
.append(violation.getLeafBean()).append(" - ")
.append(violation.getMessage());
}
}
}
String error = sb.toString();
if (!error.isEmpty()) {
LOGGER.log(Level.INFO, error);
return ww.refineResponse(error);
}
return ww.getResponse();

} catch (EJBException ex) {
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/edu/harvard/iq/dataverse/util/json/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ public void setLenient(boolean lenient) {
public Dataverse parseDataverse(JsonObject jobj) throws JsonParseException {
Dataverse dv = new Dataverse();

/**
* @todo Instead of this getMandatoryString method we should run the
* String through ConstraintValidator. See EMailValidatorTest and
* EMailValidator for examples. That way we can check not only if it's
* required or not but other bean validation rules such as "must match
* this regex".
*/
dv.setAlias(getMandatoryString(jobj, "alias"));
dv.setName(getMandatoryString(jobj, "name"));
dv.setDescription(jobj.getString("description", null));
Expand All @@ -102,7 +109,16 @@ public Dataverse parseDataverse(JsonObject jobj) throws JsonParseException {
dv.setDataverseTheme(theme);
theme.setDataverse(dv);
}


dv.setDataverseType(Dataverse.DataverseType.UNCATEGORIZED); // default
if (jobj.containsKey("dataverseType")) {
for (Dataverse.DataverseType dvtype : Dataverse.DataverseType.values()) {
if (dvtype.name().equals(jobj.getString("dataverseType"))) {
dv.setDataverseType(dvtype);
}
}
}

/* We decided that subject is not user set, but gotten from the subject of the dataverse's
datasets - leavig this code in for now, in case we need to go back to it at some point
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ public static JsonObjectBuilder json(Dataverse dv) {
.add("affiliation", dv.getAffiliation())
.add("dataverseContacts", json(dv.getDataverseContacts()))
.add("permissionRoot", dv.isPermissionRoot())
.add("description", dv.getDescription());
.add("description", dv.getDescription())
.add("dataverseType", dv.getDataverseType().name());
if (dv.getOwner() != null) {
bld.add("ownerId", dv.getOwner().getId());
}
Expand Down
119 changes: 89 additions & 30 deletions src/test/java/edu/harvard/iq/dataverse/api/DataversesIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@

import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
import edu.harvard.iq.dataverse.Dataverse;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.logging.Logger;
import org.junit.AfterClass;
import javax.json.Json;
import javax.json.JsonObject;
import javax.ws.rs.core.Response.Status;
import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.OK;
import org.junit.BeforeClass;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.equalTo;

public class DataversesIT {

private static final Logger logger = Logger.getLogger(DataversesIT.class.getCanonicalName());

private static String username1;
private static String apiToken1;
private static String dataverseAlias1;
private static String dataverseAlias2;

@BeforeClass
public static void setUpClass() {

RestAssured.baseURI = UtilIT.getRestAssuredBaseUri();

Response createUserResponse = UtilIT.createRandomUser();
// createUserResponse.prettyPrint();
assertEquals(200, createUserResponse.getStatusCode());

apiToken1 = UtilIT.getApiTokenFromResponse(createUserResponse);
username1 = UtilIT.getUsernameFromResponse(createUserResponse);

}

@Test
public void testAttemptToCreateDuplicateAlias() throws Exception {

Response createDataverse1Response = UtilIT.createRandomDataverse(apiToken1);
Response createUser = UtilIT.createRandomUser();
// createUser.prettyPrint();
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);

Response createDataverse1Response = UtilIT.createRandomDataverse(apiToken);
if (createDataverse1Response.getStatusCode() != 201) {
// purposefully using println here to the error shows under "Test Results" in Netbeans
System.out.println("A workspace for testing (a dataverse) couldn't be created in the root dataverse. The output was:\n\n" + createDataverse1Response.body().asString());
Expand All @@ -44,37 +44,96 @@ public void testAttemptToCreateDuplicateAlias() throws Exception {
}
assertEquals(201, createDataverse1Response.getStatusCode());

dataverseAlias1 = UtilIT.getAliasFromResponse(createDataverse1Response);
dataverseAlias2 = dataverseAlias1.toUpperCase();
String dataverseAlias1 = UtilIT.getAliasFromResponse(createDataverse1Response);
String dataverseAlias2 = dataverseAlias1.toUpperCase();
logger.info("Attempting to creating dataverse with alias '" + dataverseAlias2 + "' (uppercase version of existing '" + dataverseAlias1 + "' dataverse, should fail)...");
Response attemptToCreateDataverseWithDuplicateAlias = UtilIT.createDataverse(dataverseAlias2, apiToken1);
String category = null;
Response attemptToCreateDataverseWithDuplicateAlias = UtilIT.createDataverse(dataverseAlias2, category, apiToken);
attemptToCreateDataverseWithDuplicateAlias.prettyPrint();
assertEquals(400, attemptToCreateDataverseWithDuplicateAlias.getStatusCode());

logger.info("Deleting dataverse " + dataverseAlias1);
Response deleteDataverse1Response = UtilIT.deleteDataverse(dataverseAlias1, apiToken1);
Response deleteDataverse1Response = UtilIT.deleteDataverse(dataverseAlias1, apiToken);
deleteDataverse1Response.prettyPrint();
assertEquals(200, deleteDataverse1Response.getStatusCode());

logger.info("Checking response code for attempting to delete a non-existent dataverse.");
Response attemptToDeleteDataverseThatShouldNotHaveBeenCreated = UtilIT.deleteDataverse(dataverseAlias2, apiToken1);
Response attemptToDeleteDataverseThatShouldNotHaveBeenCreated = UtilIT.deleteDataverse(dataverseAlias2, apiToken);
attemptToDeleteDataverseThatShouldNotHaveBeenCreated.prettyPrint();
assertEquals(404, attemptToDeleteDataverseThatShouldNotHaveBeenCreated.getStatusCode());

}

@AfterClass
public static void tearDownClass() {
boolean disabled = false;
@Test
public void testDataverseCategory() {
Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);

Response createDataverseWithoutCategory = UtilIT.createRandomDataverse(apiToken);
createDataverseWithoutCategory.prettyPrint();
createDataverseWithoutCategory.then().assertThat()
.body("data.dataverseType", equalTo("UNCATEGORIZED"))
.statusCode(Status.CREATED.getStatusCode());

String alias1 = UtilIT.getRandomIdentifier();
String category1 = Dataverse.DataverseType.LABORATORY.toString();
Response createDataverseWithCategory = UtilIT.createDataverse(alias1, category1, apiToken);
createDataverseWithCategory.prettyPrint();
createDataverseWithCategory.then().assertThat()
.body("data.dataverseType", equalTo("LABORATORY"))
.statusCode(Status.CREATED.getStatusCode());

String alias2 = UtilIT.getRandomIdentifier();
String madeUpCategory = "madeUpCategory";
Response createDataverseWithInvalidCategory = UtilIT.createDataverse(alias2, madeUpCategory, apiToken);
createDataverseWithInvalidCategory.prettyPrint();
createDataverseWithInvalidCategory.then().assertThat()
.body("data.dataverseType", equalTo("UNCATEGORIZED"))
.statusCode(Status.CREATED.getStatusCode());

String alias3 = UtilIT.getRandomIdentifier();
String category3 = Dataverse.DataverseType.LABORATORY.toString().toLowerCase();
Response createDataverseWithLowerCaseCategory = UtilIT.createDataverse(alias3, category3, apiToken);
createDataverseWithLowerCaseCategory.prettyPrint();
createDataverseWithLowerCaseCategory.then().assertThat()
.body("data.dataverseType", equalTo("UNCATEGORIZED"))
.statusCode(Status.CREATED.getStatusCode());

if (disabled) {
return;
}
}

Response deleteUser1Response = UtilIT.deleteUser(username1);
deleteUser1Response.prettyPrint();
assertEquals(200, deleteUser1Response.getStatusCode());
@Test
public void testMinimalDataverse() throws FileNotFoundException {
Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);
JsonObject dvJson;
FileReader reader = new FileReader("doc/sphinx-guides/source/_static/api/dataverse-minimal.json");
dvJson = Json.createReader(reader).readObject();
Response create = UtilIT.createDataverse(dvJson, apiToken);
create.prettyPrint();
create.then().assertThat().statusCode(CREATED.getStatusCode());
Response deleteDataverse = UtilIT.deleteDataverse("science", apiToken);
deleteDataverse.prettyPrint();
deleteDataverse.then().assertThat().statusCode(OK.getStatusCode());
}

@Test
public void testNotEnoughJson() {
Response createUser = UtilIT.createRandomUser();
createUser.prettyPrint();
String username = UtilIT.getUsernameFromResponse(createUser);
String apiToken = UtilIT.getApiTokenFromResponse(createUser);
Response createFail = UtilIT.createDataverse(Json.createObjectBuilder().add("name", "notEnough").add("alias", "notEnough").build(), apiToken);
createFail.prettyPrint();
createFail.then().assertThat()
/**
* @todo We really don't want Dataverse to throw a 500 error
* when not enough JSON is supplied to create a dataverse.
*/
.statusCode(INTERNAL_SERVER_ERROR.getStatusCode());
}

}
41 changes: 0 additions & 41 deletions src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ public class SearchIT {
private static TestUser homer;
private static TestUser ned;
private static TestUser clancy;
private static final String categoryTestDataverse = "categoryTestDataverse";
private static final String dvForPermsTesting = "dvForPermsTesting";
private static String dataset1;
private static String dataset2;
Expand All @@ -89,7 +88,6 @@ public class SearchIT {
private static final boolean disableTestPermsonRootDv = false;
private static final boolean disableTestPermsOnNewDv = false;
private static final boolean homerPublishesVersion2AfterDeletingFile = false;
private static final boolean disableTestCategory = false;
private Stopwatch timer;
private boolean haveToUseCurlForUpload = false;

Expand Down Expand Up @@ -731,40 +729,6 @@ public void homerPublishesVersion2AfterDeletingFile() throws InterruptedExceptio

}

@Ignore
@Test
public void dataverseCategory() {

if (disableTestCategory) {
return;
}

Response enableNonPublicSearch = enableSetting(SettingsServiceBean.Key.SearchApiNonPublicAllowed);
assertEquals(200, enableNonPublicSearch.getStatusCode());

/**
* Unfortunately, it appears that the ability to specify the category of
* a dataverse when creating it is a GUI-only feature. It can't
* currently be done via the API, to our knowledge. You also can't tell
* from the API which category was persisted but it always seems to be
* "UNCATEGORIZED"
*/
TestDataverse dataverseToCreate = new TestDataverse(categoryTestDataverse, categoryTestDataverse, Dataverse.DataverseType.ORGANIZATIONS_INSTITUTIONS);
Response createDvResponse = createDataverse(dataverseToCreate, homer);
assertEquals(201, createDvResponse.getStatusCode());

TestSearchQuery query = new TestSearchQuery(categoryTestDataverse);
Response searchResponse = search(query, homer);
// searchResponse.prettyPrint();
JsonPath jsonPath = JsonPath.from(searchResponse.body().asString());
String category = jsonPath.get("data.facets." + SearchFields.DATAVERSE_CATEGORY).toString();
String msg = "category: " + category;
assertEquals("category: [null]", msg);

Response disableNonPublicSearch = deleteSetting(SettingsServiceBean.Key.SearchApiNonPublicAllowed);
assertEquals(200, disableNonPublicSearch.getStatusCode());
}

@AfterClass
public static void cleanup() {

Expand Down Expand Up @@ -805,11 +769,6 @@ public static void cleanup() {
assertEquals(200, destroyDataset.getStatusCode());
}

if (!disableTestCategory) {
Response deleteCategoryDataverseResponse = deleteDataverse(categoryTestDataverse, homer);
assertEquals(200, deleteCategoryDataverseResponse.getStatusCode());
}

if (!disableTestPermsOnNewDv) {
Response deleteDvResponse = deleteDataverse(dvForPermsTesting, homer);
assertEquals(200, deleteDvResponse.getStatusCode());
Expand Down
Loading

0 comments on commit 8ad3431

Please sign in to comment.