Skip to content

Commit

Permalink
Merge pull request #4096 from IQSS/exp-oai_create_api
Browse files Browse the repository at this point in the history
API for creating OAI-PMH sets
  • Loading branch information
kcondon committed Mar 16, 2018
2 parents 181dbd1 + f514d1a commit 10bdcb6
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 23 deletions.
3 changes: 3 additions & 0 deletions src/main/java/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,9 @@ harvestserver.editSetDialog.setspec.helptext=The name can not be changed once th
harvestserver.newSetDialog.setspec.required=Name (OAI setSpec) cannot be empty!
harvestserver.newSetDialog.setspec.invalid=Name (OAI setSpec) can contain only letters, digits, underscores (_) and dashes (-).
harvestserver.newSetDialog.setspec.alreadyused=This set name (OAI setSpec) is already used.
harvestserver.newSetDialog.setspec.sizelimit=This set name (OAI setSpec) may be no longer than 30 characters.
harvestserver.newSetDialog.setspec.superUser.required=Only superusers may create OAI sets.

harvestserver.newSetDialog.setdescription=Description
harvestserver.newSetDialog.setdescription.tip=Provide a brief description for this OAI set.
harvestserver.newSetDialog.setdescription.required=Set description cannot be empty!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,13 @@ public void validateSetSpec(FacesContext context, UIComponent toValidate, Object
if (context.getExternalContext().getRequestParameterMap().get("DO_VALIDATION") != null) {

if (!StringUtils.isEmpty(value)) {
if (value.length() > 30){
input.setValid(false);
context.addMessage(toValidate.getClientId(),
new FacesMessage(FacesMessage.SEVERITY_ERROR, "", JH.localize("harvestserver.newSetDialog.setspec.sizelimit")));
return;

}
if (!Pattern.matches("^[a-zA-Z0-9\\_\\-]+$", value)) {
input.setValid(false);
context.addMessage(toValidate.getClientId(),
Expand Down
93 changes: 73 additions & 20 deletions src/main/java/edu/harvard/iq/dataverse/api/HarvestingServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@
import edu.harvard.iq.dataverse.harvest.server.OAISet;
import edu.harvard.iq.dataverse.harvest.server.OAISetServiceBean;
import edu.harvard.iq.dataverse.util.json.JsonParseException;
import edu.harvard.iq.dataverse.authorization.users.User;
import static edu.harvard.iq.dataverse.util.JsfHelper.JH;
import javax.json.JsonObjectBuilder;
import static edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder.jsonObjectBuilder;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.application.FacesMessage;
import javax.json.Json;
import javax.json.JsonReader;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.ws.rs.DELETE;
Expand All @@ -39,6 +45,7 @@
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;

/**
*
Expand Down Expand Up @@ -104,30 +111,76 @@ public Response oaiSet(@PathParam("specname") String spec, @QueryParam("key") St
"Internal error: failed to produce output for OAI set " + spec + ".");
}
}


/**
* create an OAI set from spec in path and other parameters from POST body
* (as JSON). {"name":$set_name,
* "description":$optional_set_description,"definition":$set_search_query_string}.
*/
@POST
@Path("{specname}")
public Response createOaiSet(String jsonBody, @PathParam("specname") String spec, @QueryParam("key") String apiKey) throws IOException, JsonParseException {
/*
* authorization modeled after the UI (aka HarvestingSetsPage)
*/
AuthenticatedUser dvUser;
try {
dvUser = findAuthenticatedUserOrDie();
} catch (WrappedResponse wr) {
return wr.getResponse();
}
if (!dvUser.isSuperuser()) {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.superUser.required"));
}

StringReader rdr = new StringReader(jsonBody);

//try () {
StringReader rdr = new StringReader(jsonBody);
JsonObject json = Json.createReader(rdr).readObject();

OAISet set = new OAISet();
// TODO: check that it doesn't exist yet...
set.setSpec(spec);
// TODO: jsonParser().parseOaiSet(json, set);

oaiSetService.save(set);

return created( "/harvest/server/oaisets" + spec, oaiSetAsJson(set));

//} catch (JsonParseException ex) {
// return errorResponse( Response.Status.BAD_REQUEST, "Error parsing OAI set: " + ex.getMessage() );

//} catch (WrappedResponse ex) {
// return ex.getResponse();
//}
try( JsonReader jrdr = Json.createReader(rdr) )
{
JsonObject json = jrdr.readObject();

OAISet set = new OAISet();
//Validating spec
if (!StringUtils.isEmpty(spec)) {
if (spec.length() > 30) {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.sizelimit"));
}
if (!Pattern.matches("^[a-zA-Z0-9\\_\\-]+$", spec)) {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.invalid"));
// If it passes the regex test, check
}
if (oaiSetService.findBySpec(spec) != null) {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.alreadyused"));
}

} else {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.required"));
}
set.setSpec(spec);
String name, desc, defn;

try {
name = json.getString("name");
} catch (NullPointerException npe_name) {
return badRequest(ResourceBundle.getBundle("Bundle").getString("harvestserver.newSetDialog.setspec.required"));
}
try {
defn = json.getString("definition");
} catch (NullPointerException npe_defn) {
throw new JsonParseException("definition unspecified");
}
try {
desc = json.getString("description");
} catch (NullPointerException npe_desc) {
desc = ""; //treating description as optional
}
set.setName(name);
set.setDescription(desc);
set.setDefinition(defn);
oaiSetService.save(set);
return created("/harvest/server/oaisets" + spec, oaiSetAsJson(set));
}

}

@PUT
Expand Down
47 changes: 45 additions & 2 deletions src/main/java/edu/harvard/iq/dataverse/api/Metadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import javax.ws.rs.core.Response;
import javax.ws.rs.PathParam;
import javax.ws.rs.PUT;
import edu.harvard.iq.dataverse.harvest.server.OAISetServiceBean;
import edu.harvard.iq.dataverse.harvest.server.OAISet;

/**
*
* @author Leonid Andreev
Expand All @@ -22,7 +28,10 @@
@Path("admin/metadata")
public class Metadata extends AbstractApiBean {
private static final Logger logger = Logger.getLogger(Metadata.class.getName());


@EJB
OAISetServiceBean oaiSetService;

@EJB
DatasetServiceBean datasetService;

Expand Down Expand Up @@ -50,5 +59,39 @@ public Response exportAll() {
public Response reExportAll() {
datasetService.reExportAllAsync();
return this.accepted();
}
}

/**
* initial attempt at triggering indexing/creation/population of a OAI set without going throught
* the UI.
*/
@PUT
@Path("/exportOAI/{specname}")
public Response exportOaiSet( @PathParam("specname") String spec )
{
// assuming this belongs here (because it's a metadata export), but open to moving it elsewhere
OAISet set = null;
try
{
set = oaiSetService.findBySpec(spec);
}
catch(Exception ex)
{
return error(Response.Status.BAD_REQUEST,"bad request / invalid OAI set");
}
if ( null == set )
{
return error(Response.Status.NOT_FOUND, "unable to find specified OAI set");
}
try
{
oaiSetService.setUpdateInProgress( set.getId() );
oaiSetService.exportOaiSetAsync(set);
return ok("export started");
}
catch( Exception ex )
{
return error(Response.Status.BAD_REQUEST, "problem exporting OAI set");
}
}
}
95 changes: 95 additions & 0 deletions src/test/java/edu/harvard/iq/dataverse/api/HarvestingServerIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package edu.harvard.iq.dataverse.api;

import java.util.logging.Logger;
import com.jayway.restassured.RestAssured;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;

import com.jayway.restassured.response.Response;
import static com.jayway.restassured.RestAssured.given;

/**
* extremely minimal API tests for creating OAI sets.
*/
public class HarvestingServerIT
{
private static final Logger logger = Logger.getLogger(HarvestingServerIT.class.getCanonicalName());

@BeforeClass
public static void setUpClass()
{
RestAssured.baseURI = UtilIT.getRestAssuredBaseUri();
}

@AfterClass
public static void afterClass()
{
//intentional no-op until there's cleanup to be done
}

private void setupUsers()
{
Response cu0 = UtilIT.createRandomUser();
normalUserAPIKey = UtilIT.getApiTokenFromResponse( cu0 );
Response cu1 = UtilIT.createRandomUser();
String un1 = UtilIT.getUsernameFromResponse( cu1 );
Response u1a = UtilIT.makeSuperUser( un1 );
adminUserAPIKey = UtilIT.getApiTokenFromResponse( cu1 );
}

private String jsonForTestSpec(String name, String def)
{
String r = String.format("{\"name\":\"%s\",\"definition\":\"%s\"}",name,def);//description is optional
return r;
}

private String normalUserAPIKey;
private String adminUserAPIKey;

@Test
public void testSetCreation()
{
setupUsers();
String setName = UtilIT.getRandomString(6);
String def="*";

// make sure the set does not exist
String u0 = String.format("/api/harvest/server/oaisets/%s",setName);
Response r0 = given()
.get( u0 );
assertEquals( 404, r0.getStatusCode() );

// try to create set as normal user, should fail

Response r1 = given()
.header( UtilIT.API_TOKEN_HTTP_HEADER,normalUserAPIKey)
.body( jsonForTestSpec( setName,def) )
.post( u0 );
assertEquals( 400, r1.getStatusCode() );

// try to create set as admin user, should succeed
Response r2 = given()
.header( UtilIT.API_TOKEN_HTTP_HEADER,adminUserAPIKey)
.body( jsonForTestSpec( setName,def) )
.post( u0 );
assertEquals( 201, r2.getStatusCode() );

// try to create set with same name as admin user, should fail
Response r3 = given()
.header( UtilIT.API_TOKEN_HTTP_HEADER,adminUserAPIKey)
.body( jsonForTestSpec( setName,def) )
.post( u0 );
assertEquals( 400, r3.getStatusCode() );

// try to export set as admin user, should succeed (under admin API, not checking that normal user will fail)
String u1 = String.format("/api/admin/metadata/exportOAI/%s",setName);
Response r4 = given()
.put( u1 );
assertEquals( 200 , r4.getStatusCode() );

// TODO - get an answer to the question of if it's worth cleaning up (users, sets) or not

}
}
3 changes: 2 additions & 1 deletion src/test/java/edu/harvard/iq/dataverse/api/UtilIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ private static String getRandomUsername(String usernamePrefix) {
return usernamePrefix + getRandomIdentifier().substring(0, 8);
}

public static String getRandomString(int length) {
public static String getRandomString(int length) {
//is it worth replacing with something that doesn't error out on getRandomString(8)
if (length < 0) {
length = 3;
}
Expand Down

0 comments on commit 10bdcb6

Please sign in to comment.