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

API for creating OAI-PMH sets #4096

Merged
merged 8 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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