Skip to content

Commit

Permalink
ITs that pass with RDF4J 2.2.2, but fail with 2.2.4. (#222)
Browse files Browse the repository at this point in the history
* Use an empty base url; none of the URIs are relative in the data.

* Share HTTP-based methods for depositing and updating discos in BaseHttpIT.

* Prepare BaseHttpIT for additonal integration tests
- Utility classes with methods for manipulating RDF and working with OkHttp
- Additional properties for working with the RDF4J server

* IT which queries the RDF4J "size" endpoint, which counts the number of statements in a context.

Related to #221
  • Loading branch information
emetsger authored and karenhanson committed Feb 5, 2018
1 parent 4c73d95 commit 11c8a92
Show file tree
Hide file tree
Showing 7 changed files with 462 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public static Map<RMapObjectType, Set<TestUtils.RDFResource>> populateTriplestor
RepositoryConnection c = triplestore.getConnection();
) {
assertTrue(c.isOpen());
c.add(in, "http://foo/bar", source.getRdfFormat());
c.add(in, "", source.getRdfFormat());
} catch (IOException e) {
e.printStackTrace(System.err);
fail("Unexpected IOException");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public void setUp() throws Exception {
// Create necessary agents
createAgents(rmapObjects);

addTriplesFromResource("http://foo/bar", triplestore, rmapObjects);
addTriplesFromResource("", triplestore, rmapObjects);
triplestoreInitialized = true;

events = getRmapObjects(rmapObjects, RMapObjectType.EVENT, rdfHandler, comparing(RMapEvent::getStartTime));
Expand Down
101 changes: 101 additions & 0 deletions integration/src/test/java/info/rmapproject/integration/BaseHttpIT.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
package info.rmapproject.integration;

import info.rmapproject.indexing.solr.repository.DiscoRepository;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.Buffer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.RDFWriter;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.rio.helpers.StatementCollector;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
Expand All @@ -11,11 +26,20 @@
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.logging.Level;

import static info.rmapproject.integration.OkHttpUtil.getBody;
import static info.rmapproject.integration.OkHttpUtil.hasBody;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

Expand All @@ -40,6 +64,10 @@ public abstract class BaseHttpIT {

static String apiCtxPath = System.getProperty("rmap.api.context");

static String rdf4jCtxPath = System.getProperty("rdf4j.http.context");

static String rdf4jRepoName = System.getProperty("rdf4jhttp.repository.name");

static URL apiBaseUrl;

static URL appBaseUrl;
Expand All @@ -52,6 +80,8 @@ public abstract class BaseHttpIT {

static String APPLICATION_RDFXML = "application/rdf+xml";

static String APPLICATION_NQUADS = "application/n-quads";

@Autowired
OkHttpClient http;

Expand All @@ -69,6 +99,8 @@ public static void setUpBaseUrls() throws Exception {
Integer.parseInt(port) > 0);
assertNotNull("System property 'rmap.webapp.context' must be specified.", webappCtxPath);
assertNotNull("System property 'rmap.api.context' must be specified.", apiCtxPath);
assertNotNull("System property 'rdf4j.http.context' must be specified.", rdf4jCtxPath);
assertNotNull("System property 'rdf4jhttp.repository.name' must be specified.", rdf4jRepoName);
apiBaseUrl = new URL(scheme, host, Integer.parseInt(port), apiCtxPath);
appBaseUrl = new URL(scheme, host, Integer.parseInt(port), webappCtxPath);
discosEndpoint = URI.create(apiBaseUrl.toString() + "/discos");
Expand All @@ -78,4 +110,73 @@ static String encodeAuthCreds(String accessKey, String secret) {
return Base64.getEncoder().encodeToString(String.valueOf(accessKey + ":" + secret).getBytes());
}

/**
* Deposit the DiSCO by performing a {@code POST} to the {@code endpoint}. The {@code discoBody} must be encoded as
* rdf/xml. If the request is successful, the return should be a URI to the newly created DiSCO.
*
* @param endpoint the /discos API endpoint; /discos/{uri} may be used to create a new version of the disco at
* /discos/{uri}
* @param discoBody the DiSCO to deposit, encoded as rdf/xml
* @return a URI to the newly created DiSCO
* @throws IOException
*/
String depositDisco(URL endpoint, String discoBody) throws IOException {
return decorateAndExecuteRequest(endpoint,
new Request.Builder()
.post(RequestBody.create(MediaType.parse(APPLICATION_RDFXML), discoBody))
.url(endpoint),
201);
}

/**
* Delete (a.k.a. "tombstone", "soft delete") the DiSCO by sending a {@code DELETE} request to the {@code endpoint}.
*
* @param endpoint a URL that identifies the DiSCO to delete; /discos/{uri}
* @throws IOException
*/
void deleteDisco(URL endpoint) throws IOException {
decorateAndExecuteRequest(endpoint,
new Request.Builder()
.delete()
.url(endpoint),
200);
}

/**
* Executes the supplied request and verifies the response code. The request is decorated with authentication
* credentials before being sent.
*
* @param endpoint the HTTP endpoint
* @param reqBuilder the request builder, which is complete except for authentication credentials
* @param expectedStatus the status that is expected upon successful execution
* @return the body of the response as a String
* @throws IOException
*/
String decorateAndExecuteRequest(URL endpoint, Request.Builder reqBuilder, int expectedStatus) throws IOException {
String body;

Request req = reqBuilder.addHeader("Authorization", "Basic " + encodeAuthCreds(accessKey, secret))
.build();
try (Response res = http.newCall(req).execute()) {
assertEquals("'" + reqBuilder.build().method() + "' to '" + endpoint + "' failed with: '" + res.code() + "', '" + res.message() + "'" + (hasBody(reqBuilder) ? " with body \n[" + IOUtils.toString(getBody(reqBuilder), "UTF-8") + "] " : ""),
expectedStatus, res.code());
body = res.body().string();
}

return body;
}

/**
* Converts the supplied {@code discoUri} to an HTTP URL by encoding {@code discoUri} and appending it to
* {@link #discosEndpoint}. The returned URL is not guaranteed to exist.
*
* @param discoUri the URI to a DiSCO
* @return the HTTP URL for the DiSCO
* @throws MalformedURLException
* @throws UnsupportedEncodingException
*/
static URL encodeDiscoUriAsUrl(String discoUri) throws MalformedURLException, UnsupportedEncodingException {
return URI.create(discosEndpoint.toString() + "/" + URLEncoder.encode(discoUri, "UTF-8")).toURL();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package info.rmapproject.integration;

import okhttp3.Request;
import okio.Buffer;
import org.apache.commons.io.input.NullInputStream;

import java.io.IOException;
import java.io.InputStream;

/**
* Utilities for working with the OkHttp platform.
*/
class OkHttpUtil {

/**
* Returns true if the supplied request builder has a body.
*
* @param reqBuilder
* @return
*/
static boolean hasBody(Request.Builder reqBuilder) {
Request req = reqBuilder.build();
return req.body() != null;
}

/**
* Obtain an InputStream to the request body.
*
* @param reqBuilder the request builder that may have a body
* @return a (potentially empty) InputStream for the request body
*/
static InputStream getBody(Request.Builder reqBuilder) {
if (!hasBody(reqBuilder)) {
return new NullInputStream(0L);
}

Request req = reqBuilder.build();
Buffer buf = new Buffer();
try {
req.body().writeTo(buf);
} catch (IOException e) {
throw new RuntimeException("Unable to read HTTP body: " + e.getMessage(), e);
}

return buf.inputStream();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package info.rmapproject.integration;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.RepositoryResult;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.RDFParser;
import org.eclipse.rdf4j.rio.RDFWriter;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.rio.helpers.StatementCollector;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Utility methods for working with the RDF4J platform.
*/
class Rdf4jUtil {

/**
* Reads the supplied RDF into an RDF4J {@link Model}.
*
* @param rdfIn the RDF to read into the {@code Model}
* @param format the format that the RDF is in
* @param baseUri the URI used to resolve any relative URI references in the supplied RDF
* @return
*/
static Model readModel(InputStream rdfIn, RDFFormat format, String baseUri) {
RDFParser parser = Rio.createParser(format);
Model model = new LinkedHashModel();
parser.setRDFHandler(new StatementCollector(model));

try {
parser.parse(rdfIn, baseUri);
} catch (IOException e) {
throw new RuntimeException("Unable to create an RDF Model: " + e.getMessage(), e);
}

return model;
}

/**
* Converts RDF from the format in {@code fromFormat} to the format specified by {@code toFormat}.
*
* @param from the source RDF
* @param fromFormat the format of the source RDF
* @param baseUri the base URI used to resolve any relative URI references in the source RDF
* @param toFormat the format to convert the RDF to
* @return an InputStream to the converted RDF
*/
static InputStream convertRdf(InputStream from, RDFFormat fromFormat, String baseUri, RDFFormat toFormat) {
if (fromFormat == toFormat) {
return from;
}
ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
Model m = readModel(from, fromFormat, baseUri);
RDFWriter writer = Rio.createWriter(toFormat, out);
writer.startRDF();
m.forEach(writer::handleStatement);
writer.endRDF();
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("Unable to close output stream after writing RDF: " + e.getMessage(), e);
}

return new ByteArrayInputStream(out.toByteArray());
}


/**
* Dumps the contents of the triplestore to the provided output stream.
*
* @param connection the connection to the triplestore
* @param outputStream the output stream which contains the dumped triples
*/
public static void dumpTriplestore(RepositoryConnection connection, OutputStream outputStream) {
RepositoryResult<Statement> result = connection.getStatements(null, null, null, true);
while (result.hasNext()) {
try {
outputStream.write(result.next().toString().getBytes("UTF-8"));
} catch (IOException e) {
throw new RuntimeException("Error encoding bytes from the triplestore to UTF-8: " + e.getMessage(), e);
}

try {
outputStream.write("\n".getBytes());
} catch (IOException e) {
throw new RuntimeException("Error writing the triples to the output stream: " + e.getMessage(), e);
}
}
}

}
Loading

0 comments on commit 11c8a92

Please sign in to comment.